aboutsummaryrefslogtreecommitdiff
# Copyright (C) University of Tennessee Health Science Center, Memphis, TN.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License
# as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# This program is available from Source Forge: at GeneNetwork Project
# (sourceforge.net/projects/genenetwork/).
#
# Contact Drs. Robert W. Williams and Xiaodong Zhou (2010)
# at rwilliams@uthsc.edu and xzhou15@uthsc.edu
#
#
#
# This module is used by GeneNetwork project (www.genenetwork.org)
#
# Created by GeneNetwork Core Team 2010/08/10
#
# Last updated by Zach 12/14/2010

import datetime
import string
from math import *
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
from PIL import ImageColor
import os
import json

import htmlgen as HT

from gn2.base import webqtlConfig
from gn2.base.GeneralObject import GeneralObject
from gn2.utility import webqtlUtil
from gn2.utility import Plot
from gn2.utility.tools import get_setting
from gn2.wqflask.interval_analyst import GeneUtil
from gn2.base.webqtlConfig import GENERATED_IMAGE_DIR
from gn2.utility.pillow_utils import draw_rotated_text, draw_open_polygon
from gn2.wqflask.database import database_connection

try:  # Only import this for Python3
    from functools import reduce
except:
    pass

RED = ImageColor.getrgb("red")
BLUE = ImageColor.getrgb("blue")
GRAY = ImageColor.getrgb("gray")
GOLD = ImageColor.getrgb("gold")
BLACK = ImageColor.getrgb("black")
GREEN = ImageColor.getrgb("green")
PURPLE = ImageColor.getrgb("purple")
ORANGE = ImageColor.getrgb("orange")
YELLOW = ImageColor.getrgb("yellow")
DARKRED = ImageColor.getrgb("darkred")
DARKBLUE = ImageColor.getrgb("darkblue")
DARKGRAY = ImageColor.getrgb("darkgray")
DEEPPINK = ImageColor.getrgb("deeppink")
DARKGREEN = ImageColor.getrgb("darkgreen")
GAINSBORO = ImageColor.getrgb("gainsboro")
LIGHTBLUE = ImageColor.getrgb("lightblue")
DARKORANGE = ImageColor.getrgb("darkorange")
DARKVIOLET = ImageColor.getrgb("darkviolet")
MEDIUMPURPLE = ImageColor.getrgb("mediumpurple")
# ---- END: Define common colours ---- #

# ZS: List of distinct colors for manhattan plot if user selects "varied"
COLOR_CODES = ["#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF",
               "#000000", "#800000", "#008000", "#000080", "#808000", "#800080",
               "#008080", "#808080", "#C00000", "#00C000", "#0000C0", "#C0C000",
               "#C000C0", "#00C0C0", "#C0C0C0", "#400000", "#004000", "#000040"]

DISTINCT_COLOR_LIST = [ImageColor.getrgb(color) for color in COLOR_CODES]

# ---- FONT FILES ---- #
VERDANA_FILE = "./gn2/wqflask/static/fonts/verdana.ttf"
VERDANA_BOLD_FILE = "./gn2/wqflask/static/fonts/verdanab.ttf"
TREBUC_FILE = "./gn2/wqflask/static/fonts/trebucbd.ttf"
FNT_BS_FILE = "./gn2/wqflask/static/fonts/fnt_bs.ttf"
ARIAL_FILE = "./gn2/wqflask/static/fonts/arial.ttf"

assert(os.path.isfile(VERDANA_FILE))

class HtmlGenWrapper:
    """Wrapper Methods for HTML gen"""
    @staticmethod
    def create_image_tag(**kwargs):
        image = HT.Image("", "")
        for key, value in list(kwargs.items()):
            image.set_attribute(key, value)
        return image

    @staticmethod
    def create_form_tag(**kwargs):
        form = HT.Form("POST", "")  # Default method is POST

        for key, value in list(kwargs.items()):
            if key == "submit":
                form.append(value)
                continue
            form.set_attribute(key.replace("cgi", "action"), str(value))
        return form

    @staticmethod
    def create_p_tag(**kwargs):
        paragraph = HT.Paragraph()
        for key, value in list(kwargs.items()):
            paragraph.set_attribute(key, value)
        return paragraph

    @staticmethod
    def create_br_tag():
        return HT.VoidElement("br")

    @staticmethod
    def create_input_tag(**kwargs):
        input_ = HT.Input()
        for key, value in list(kwargs.items()):
            input_.set_attribute(key.lower().replace("_", ""), value)
        return input_

    @staticmethod
    def create_area_tag(**kwargs):
        area = HT.VoidElement("area")
        for key, value in list(kwargs.items()):
            area.set_attribute(key, value)
        return area

    @staticmethod
    def create_link_tag(href, content, **kwargs):
        link = HT.Link(href, content)
        for key, value in list(kwargs.items()):
            link.set_attribute(key, value)
        return link

    @staticmethod
    def create_map_tag(**kwargs):
        map_ = HT.Element("map")
        for key, value in list(kwargs.items()):
            map_.set_attribute(key, value)
        return map_


class DisplayMappingResults:
    """Inteval Mapping Plot Page"""
    cMGraphInterval = 5
    GRAPH_MIN_WIDTH = 900
    GRAPH_MAX_WIDTH = 10000  # Don't set this too high
    GRAPH_DEFAULT_WIDTH = 1280
    MULT_GRAPH_DEFAULT_WIDTH = 2000
    MULT_GRAPH_MIN_WIDTH = 1400
    MULT_GRAPH_DEFAULT_WIDTH = 1600
    GRAPH_DEFAULT_HEIGHT = 600

    # Display order:
    # UCSC BAND =========
    # ENSEMBL BAND -=-=-=
    # ** GENES **********
    BAND_SPACING = 4

    BAND_HEIGHT = 10
    BAND_HEIGHT = 10
    BAND_HEIGHT = 10

    NUM_GENE_ROWS = 10
    EACH_GENE_HEIGHT = 6  # number of pixels tall, for each gene to display
    EACH_GENE_ARROW_WIDTH = 5
    EACH_GENE_ARROW_SPACING = 14
    DRAW_DETAIL_MB = 4
    DRAW_UTR_LABELS_MB = 4

    qmarkImg = HtmlGenWrapper.create_image_tag(
        src='/images/qmarkBoxBlue.gif',
        width="10", height="13", border="0", alt='Glossary'
    )

    # Note that "qmark.gif" is a similar, smaller, rounded-edges
    # question mark. It doesn't look like the ones on the image,
    # though, which is why we don't use it here.

    HELP_WINDOW_NAME = 'helpWind'

    # BEGIN HaplotypeAnalyst
    NR_INDIVIDUALS = 0
    # END HaplotypeAnalyst

    ALEX_DEBUG_BOOL_PRINT_GENE_LIST = 1

    kONE_MILLION = 1000000

    LODFACTOR = 4.61

    SNP_COLOR = ORANGE  # Color for the SNP "seismograph"
    TRANSCRIPT_LOCATION_COLOR = MEDIUMPURPLE

    BOOTSTRAP_BOX_COLOR = YELLOW
    LRS_COLOR = ImageColor.getrgb("#0000FF")
    SIGNIFICANT_COLOR = ImageColor.getrgb("#EBC7C7")
    SUGGESTIVE_COLOR = GAINSBORO
    SIGNIFICANT_WIDTH = 5
    SUGGESTIVE_WIDTH = 5
    ADDITIVE_COLOR_POSITIVE = GREEN
    ADDITIVE_COLOR_NEGATIVE = ORANGE
    DOMINANCE_COLOR_POSITIVE = DARKVIOLET
    DOMINANCE_COLOR_NEGATIVE = RED

    # BEGIN HaplotypeAnalyst
    HAPLOTYPE_POSITIVE = BLUE
    HAPLOTYPE_NEGATIVE = RED
    HAPLOTYPE_HETEROZYGOUS = ORANGE
    HAPLOTYPE_RECOMBINATION = DARKGRAY
    # END HaplotypeAnalyst

    TOP_RIGHT_INFO_COLOR = BLACK

    CLICKABLE_WEBQTL_REGION_COLOR = ImageColor.getrgb("#F5D3D3")
    CLICKABLE_WEBQTL_REGION_OUTLINE_COLOR = ImageColor.getrgb("#FCE9E9")
    CLICKABLE_WEBQTL_TEXT_COLOR = ImageColor.getrgb("#912828")

    CLICKABLE_PHENOGEN_REGION_COLOR = ImageColor.getrgb("#A2FB94")
    CLICKABLE_PHENOGEN_REGION_OUTLINE_COLOR = ImageColor.getrgb("#CEFEC7")
    CLICKABLE_PHENOGEN_TEXT_COLOR = ImageColor.getrgb("#1FD504")

    CLICKABLE_UCSC_REGION_COLOR = ImageColor.getrgb("#DDDDEE")
    CLICKABLE_UCSC_REGION_OUTLINE_COLOR = ImageColor.getrgb("#EDEDFF")
    CLICKABLE_UCSC_TEXT_COLOR = ImageColor.getrgb("#333366")

    CLICKABLE_ENSEMBL_REGION_COLOR = ImageColor.getrgb("#EEEEDD")
    CLICKABLE_ENSEMBL_REGION_OUTLINE_COLOR = ImageColor.getrgb("#FEFEEE")
    CLICKABLE_ENSEMBL_TEXT_COLOR = ImageColor.getrgb("#555500")

    GRAPH_BACK_LIGHT_COLOR = ImageColor.getrgb("#FBFBFF")
    GRAPH_BACK_DARK_COLOR = ImageColor.getrgb("#F1F1F9")

    HELP_PAGE_REF = '/glossary.html'

    def __init__(self, start_vars):
        self.temp_uuid = start_vars['temp_uuid']
        self.hash_of_inputs = start_vars['hash_of_inputs']
        self.dataid = start_vars['dataid']

        self.dataset = start_vars['dataset']
        self.this_trait = start_vars['this_trait']
        self.n_samples = start_vars['n_samples']
        self.species = start_vars['species']
        self.genofile_string = ""
        if 'genofile_string' in start_vars:
            self.genofile_string = start_vars['genofile_string']

        self.geno_db_exists = start_vars['geno_db_exists']

        self.first_run = True
        if 'first_run' in start_vars:
            self.first_run = start_vars['first_run']

        if 'temp_trait' in start_vars and start_vars['temp_trait'] != "False":
            self.temp_trait = "True"
            self.group = start_vars['group']

        # Needing for form submission when doing single chr
        # mapping or remapping after changing options
        self.sample_vals = start_vars['sample_vals']
        self.vals_hash= start_vars['vals_hash']
        self.sample_vals_dict = json.loads(self.sample_vals)

        self.transform = start_vars['transform']
        self.mapping_method = start_vars['mapping_method']
        self.mapping_results_path = start_vars['mapping_results_path']
        if self.mapping_method == "rqtl_geno":
            self.mapmethod_rqtl = start_vars['method']
            self.mapmodel_rqtl = start_vars['model']
            self.pair_scan = start_vars['pair_scan']

        self.js_data = start_vars['js_data']
        # Top markers to display in table
        self.trimmed_markers = start_vars['trimmed_markers']

        if self.dataset.group.species == "rat":
            self._ucscDb = "rn6"
        elif self.dataset.group.species == "mouse":
            self._ucscDb = "mm10"
        else:
            self._ucscDb = ""

        #####################################
        # Options
        #####################################
        # Mapping options
        if start_vars['mapping_scale'] != "":
            self.plotScale = start_vars['mapping_scale']
        else:
            self.plotScale = "physic"

        self.manhattan_plot = start_vars['manhattan_plot']
        if self.manhattan_plot:
            self.color_scheme = "alternating"
            if 'color_scheme' in start_vars:
                self.color_scheme = start_vars['color_scheme']
                if self.color_scheme == "single":
                    self.manhattan_single_color = ImageColor.getrgb(
                        "#" + start_vars['manhattan_single_color'])

        if 'permCheck' in list(start_vars.keys()):
            self.permChecked = start_vars['permCheck']
        else:
            self.permChecked = False
        if start_vars['num_perm'] > 0:
            self.nperm = int(start_vars['num_perm'])
            if self.permChecked:
                self.perm_output = start_vars['perm_output']
                self.suggestive = start_vars['suggestive']
                self.significant = start_vars['significant']
        else:
            self.nperm = 0

        if 'bootCheck' in list(start_vars.keys()):
            self.bootChecked = start_vars['bootCheck']
        else:
            self.bootChecked = False
        if 'num_bootstrap' in list(start_vars.keys()):
            self.nboot = int(start_vars['num_bootstrap'])
        else:
            self.nboot = 0
        if 'bootstrap_results' in list(start_vars.keys()):
            self.bootResult = start_vars['bootstrap_results']
        else:
            self.bootResult = []

        if 'do_control' in list(start_vars.keys()):
            self.doControl = start_vars['do_control']
        else:
            self.doControl = "false"
        if 'control_marker' in list(start_vars.keys()):
            self.controlLocus = start_vars['control_marker']
        else:
            self.controlLocus = ""
        if 'covariates' in list(start_vars.keys()):
            self.covariates = start_vars['covariates']
        if 'maf' in list(start_vars.keys()):
            self.maf = start_vars['maf']
        else:
            self.maf = ""
        if 'output_files' in list(start_vars.keys()):
            self.output_files = start_vars['output_files']
        if 'use_loco' in list(start_vars.keys()) and self.mapping_method == "gemma":
            self.use_loco = start_vars['use_loco']

        if self.mapping_method == "reaper":
            if 'output_files' in start_vars:
                self.output_files = ",".join(
                    [(the_file if the_file is not None else "") for the_file in start_vars['output_files']])

        self.categorical_vars = ""
        self.perm_strata = ""
        if 'perm_strata' in list(start_vars.keys()) and 'categorical_vars' in list(start_vars.keys()):
            self.categorical_vars = start_vars['categorical_vars']
            self.perm_strata = start_vars['perm_strata']

        self.selectedChr = int(start_vars['selected_chr'])

        self.strainlist = start_vars['samples']

        self.traitList = []
        thisTrait = start_vars['this_trait']
        self.traitList.append(thisTrait)

        ################################################################
        # Calculations QTL goes here
        ################################################################
        self.multipleInterval = len(self.traitList) > 1
        self.qtlresults = start_vars['qtl_results']

        if self.multipleInterval:
            self.colorCollection = Plot.colorSpectrum(len(self.qtlresults))
        else:
            self.colorCollection = [self.LRS_COLOR]

        self.dataset.group.genofile = self.genofile_string.split(":")[0]
        if self.mapping_method == "reaper" and self.manhattan_plot != True:
            self.genotype = self.dataset.group.read_genotype_file(
                use_reaper=True)
        else:
            self.genotype = self.dataset.group.read_genotype_file()

        # Drawing Options
        try:
            if self.selectedChr > -1:
                self.graphWidth = min(self.GRAPH_MAX_WIDTH, max(
                    self.GRAPH_MIN_WIDTH, int(start_vars['graphWidth'])))
            else:
                self.graphWidth = min(self.GRAPH_MAX_WIDTH, max(
                    self.MULT_GRAPH_MIN_WIDTH, int(start_vars['graphWidth'])))
        except:
            if self.selectedChr > -1:
                self.graphWidth = self.GRAPH_DEFAULT_WIDTH
            else:
                self.graphWidth = self.MULT_GRAPH_DEFAULT_WIDTH

# BEGIN HaplotypeAnalyst
        if 'haplotypeAnalystCheck' in list(start_vars.keys()):
            self.haplotypeAnalystChecked = start_vars['haplotypeAnalystCheck']
        else:
            self.haplotypeAnalystChecked = False
# END HaplotypeAnalyst

        self.graphHeight = self.GRAPH_DEFAULT_HEIGHT
        self.dominanceChecked = False
        if 'LRSCheck' in list(start_vars.keys()):
            self.LRS_LOD = start_vars['LRSCheck']
        else:
            self.LRS_LOD = start_vars['score_type']
        self.intervalAnalystChecked = True
        self.draw2X = False
        if 'additiveCheck' in list(start_vars.keys()):
            self.additiveChecked = start_vars['additiveCheck']
        else:
            self.additiveChecked = False
        if 'viewLegend' in list(start_vars.keys()):
            self.legendChecked = start_vars['viewLegend']
        else:
            self.legendChecked = False
        if 'showSNP' in list(start_vars.keys()):
            self.SNPChecked = start_vars['showSNP']
        else:
            self.SNPChecked = False
        if 'showHomology' in list(start_vars.keys()):
            self.homologyChecked = start_vars['showHomology']
        else:
            self.homologyChecked = "ON"
        if 'showGenes' in list(start_vars.keys()):
            self.geneChecked = start_vars['showGenes']
        else:
            self.geneChecked = False
        try:
            self.startMb = float(start_vars['startMb'])
        except:
            self.startMb = -1
        try:
            self.endMb = float(start_vars['endMb'])
        except:
            self.endMb = -1
        try:
            self.lrsMax = float(start_vars['lrsMax'])
        except:
            self.lrsMax = 0

        # Trait Infos
        self.identification = ""

        ################################################################
        # Generate Chr list and Retrieve Length Information
        ################################################################
        self.ChrList = [("All", -1)]
        for i, indChr in enumerate(self.genotype):
            if self.dataset.group.species == "mouse" and indChr.name == "20":
                self.ChrList.append(("X", i))
            elif self.dataset.group.species == "rat" and indChr.name == "21":
                self.ChrList.append(("X", i))
            self.ChrList.append((indChr.name, i))
        with database_connection(get_setting("SQL_URI")) as conn, conn.cursor() as cursor:
            cursor.execute("SELECT Length FROM Chr_Length, InbredSet "
                           "WHERE Chr_Length.SpeciesId = InbredSet.SpeciesId "
                           "AND InbredSet.Name = %s AND Chr_Length.Name IN "
                           f"({', '.join(['%s' for x in self.ChrList[1:]])}) "
                           "ORDER BY Chr_Length.OrderId",
                           (self.dataset.group.name,
                            *[x[0] for x in self.ChrList[1:]],))
            self.ChrLengthMbList = cursor.fetchall()

        self.ChrLengthMbList = [x[0] / 1000000.0 for x in self.ChrLengthMbList]
        self.ChrLengthMbSum = reduce(
            lambda x, y: x + y, self.ChrLengthMbList, 0.0)
        if self.ChrLengthMbList:
            self.MbGraphInterval = self.ChrLengthMbSum / \
                (len(self.ChrLengthMbList) * 12)  # Empirical Mb interval
        else:
            self.MbGraphInterval = 1

        self.ChrLengthCMList = []
        for i, _chr in enumerate(self.genotype):
            self.ChrLengthCMList.append(_chr[-1].cM - _chr[0].cM)

        self.ChrLengthCMSum = reduce(
            lambda x, y: x + y, self.ChrLengthCMList, 0.0)

        if self.plotScale == 'physic':
            self.GraphInterval = self.MbGraphInterval  # Mb
        else:
            self.GraphInterval = self.cMGraphInterval  # cM

        #########################
        # Get the sorting column
        #########################
        RISet = self.dataset.group.name
        if RISet in ('AXB', 'BXA', 'AXBXA'):
            self.diffCol = ['B6J', 'A/J']
        elif RISet in ('BXD', 'BXD300', 'B6D2F2', 'BDF2-2005', 'BDF2-1999', 'BHHBF2', 'BXD-Harvested', 'BXD-Longevity', 'BXD-Micturition', 'BXD-AE', 'BXD-NIA-AD', 'B6D2RI', 'BXD-Bone', 'DOD-BXD-GWI', 'BXD-Heart-Metals', 'UTHSC-Cannabinoid'):
            self.diffCol = ['B6J', 'D2J']
        elif RISet in ('CXB'):
            self.diffCol = ['CBY', 'B6J']
        elif RISet in ('BXH', 'BHF2'):
            self.diffCol = ['B6J', 'C3H']
        elif RISet in ('B6BTBRF2'):
            self.diffCol = ['B6J', 'BTB']
        elif RISet in ('LXS'):
            self.diffCol = ['ILS', 'ISS']
        else:
            self.diffCol = []

        for i, strain in enumerate(self.diffCol):
            with database_connection(get_setting("SQL_URI")) as conn, conn.cursor() as cursor:
                cursor.execute("SELECT Id FROM Strain WHERE Symbol = %s",
                               (strain,))
                if result := cursor.fetchone():
                    self.diffCol[i] = result[0]

        ################################################################
        # GeneCollection goes here
        ################################################################
        if self.plotScale == 'physic' and self.selectedChr != -1:
            #StartMb or EndMb
            if self.startMb < 0 or self.endMb < 0:
                self.startMb = 0
                self.endMb = self.ChrLengthMbList[self.selectedChr - 1]

        geneTable = ""

        self.geneCol = None
        self.homology = None
        if self.plotScale == 'physic' and self.selectedChr > -1 and (self.intervalAnalystChecked or self.geneChecked or self.homologyChecked):
            # Draw the genes for this chromosome / region of this chromosome
            webqtldatabase = self.dataset.name

            if self.dataset.group.species == "mouse":
                if self.selectedChr == 20:
                    chrName = "X"
                else:
                    chrName = self.selectedChr
                self.geneCol = GeneUtil.loadGenes(
                    str(chrName), self.diffCol, self.startMb, self.endMb, "mouse")
                self.homology = GeneUtil.load_homology(str(chrName), self.startMb, self.endMb, "mouse_to_human.csv")

            elif self.dataset.group.species == "rat":
                if self.selectedChr == 21:
                    chrName = "X"
                else:
                    chrName = self.selectedChr
                self.geneCol = GeneUtil.loadGenes(
                    str(chrName), self.diffCol, self.startMb, self.endMb, "rat")

            if self.geneCol and self.intervalAnalystChecked:
                #######################################################################
                #Nick use GENEID as RefGene to get Literature Correlation Informations#
                #For Interval Mapping, Literature Correlation isn't useful, so skip it#
                #through set GENEID is None                                           #
                #######################################################################

                GENEID = None

                self.geneTable(self.geneCol, GENEID)

# BEGIN HaplotypeAnalyst
# count the amount of individuals to be plotted, and increase self.graphHeight
        if self.haplotypeAnalystChecked and self.selectedChr > -1:
            thisTrait = self.this_trait
            smd = []
            for sample in self.sample_vals_dict.keys():
                if self.sample_vals_dict[sample] != "x":
                    temp = GeneralObject(name=sample, value=float(
                        self.sample_vals_dict[sample]))
                    smd.append(temp)
                else:
                    continue
            samplelist = list(self.genotype.prgy)
            for j, _geno in enumerate(self.genotype[0][1].genotype):
                for item in smd:
                    if item.name == samplelist[j]:
                        self.NR_INDIVIDUALS = self.NR_INDIVIDUALS + 1
# default:
            self.graphHeight = self.graphHeight + 2 * \
                (self.NR_INDIVIDUALS + 10) * self.EACH_GENE_HEIGHT
# END HaplotypeAnalyst

        if self.homologyChecked and self.homology and self.geneChecked and self.geneCol:
            self.graphHeight = self.graphHeight + (self.NUM_GENE_ROWS) * self.EACH_GENE_HEIGHT
        if self.geneChecked and self.geneCol:
            self.graphHeight = self.graphHeight + (self.NUM_GENE_ROWS) * self.EACH_GENE_HEIGHT


        ################################################################
        # Plots goes here
        ################################################################
        showLocusForm = ""
        intCanvas = Image.new("RGBA", size=(self.graphWidth, self.graphHeight))
        gifmap = self.plotIntMapping(
            intCanvas, startMb=self.startMb, endMb=self.endMb, showLocusForm=showLocusForm)

        self.gifmap = gifmap.__str__()

        self.filename = webqtlUtil.genRandStr("Itvl_")
        intCanvas.save(
            "{}.png".format(
                os.path.join(webqtlConfig.GENERATED_IMAGE_DIR, self.filename)),
            format='png')
        intImg = HtmlGenWrapper.create_image_tag(
            src="/image/{}.png".format(self.filename),
            border="0", usemap='#WebQTLImageMap'
        )

        # Scales plot differently for high resolution
        if self.draw2X:
            intCanvasX2 = Image.new("RGBA", size=(
                self.graphWidth * 2, self.graphHeight * 2))
            gifmapX2 = self.plotIntMapping(
                intCanvasX2, startMb=self.startMb, endMb=self.endMb, showLocusForm=showLocusForm, zoom=2)
            intCanvasX2.save(
                "{}.png".format(
                    os.path.join(webqtlConfig.GENERATED_IMAGE_DIR,
                                 self.filename + "X2")),
                format='png')

        ################################################################
        # Outputs goes here
        ################################################################
        # this form is used for opening Locus page or trait page, only available for genetic mapping
        if showLocusForm:
            showLocusForm = HtmlGenWrapper.create_form_tag(
                cgi=os.path.join(webqtlConfig.CGIDIR, webqtlConfig.SCRIPTFILE),
                enctype='multipart/form-data',
                name=showLocusForm,
                submit=HtmlGenWrapper.create_input_tag(type_='hidden'))

            hddn = {'FormID': 'showDatabase', 'ProbeSetID': '_', 'database': fd.RISet + \
                    "Geno", 'CellID': '_', 'RISet': fd.RISet, 'incparentsf1': 'ON'}
            for key in hddn.keys():
                showLocusForm.append(HtmlGenWrapper.create_input_tag(
                    name=key, value=hddn[key], type_='hidden'))
            showLocusForm.append(intImg)
        else:
            showLocusForm = intImg

        if (self.permChecked and self.nperm > 0) and not (self.multipleInterval and 0 < self.nperm):
            self.perm_filename = self.drawPermutationHistogram()

        ################################################################
        # footnote goes here
        ################################################################
        # Small('More information about this graph is available here.')
        btminfo = HtmlGenWrapper.create_p_tag(id="smallsize")

        if self.traitList and self.traitList[0].dataset and self.traitList[0].dataset.type == 'Geno':
            btminfo.append(HtmlGenWrapper.create_br_tag())
            btminfo.append(
                'Mapping using genotype data as a trait will result in infinity LRS at one locus. In order to display the result properly, all LRSs higher than 100 are capped at 100.')

    def plotIntMapping(self, canvas, offset=(80, 120, 110, 100), zoom=1, startMb=None, endMb=None, showLocusForm=""):
        im_drawer = ImageDraw.Draw(canvas)
        # calculating margins
        xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
        if self.multipleInterval:
            yTopOffset = max(90, yTopOffset)
        else:
            if self.legendChecked:
                yTopOffset += 10
                if self.covariates != "" and self.controlLocus and self.doControl != "false":
                    yTopOffset += 25
                if len(self.transform) > 0:
                    yTopOffset += 5
            else:
                pass

        if self.plotScale != 'physic':
            yBottomOffset = max(120, yBottomOffset)
        fontZoom = zoom
        if zoom == 2:
            xLeftOffset += 20
            fontZoom = 1.5

        xLeftOffset = int(xLeftOffset * fontZoom)
        xRightOffset = int(xRightOffset * fontZoom)
        yBottomOffset = int(yBottomOffset * fontZoom)

        cWidth = canvas.size[0]
        cHeight = canvas.size[1]
        plotWidth = cWidth - xLeftOffset - xRightOffset
        plotHeight = cHeight - yTopOffset - yBottomOffset

        # Drawing Area Height
        drawAreaHeight = plotHeight
        if self.plotScale == 'physic' and self.selectedChr > -1:
            if self.dataset.group.species == "mouse" or self.dataset.group.species == "rat":
                drawAreaHeight -= 4 * self.BAND_HEIGHT + 4 * self.BAND_SPACING + 10 * zoom
            else:
                drawAreaHeight -= 3 * self.BAND_HEIGHT + 3 * self.BAND_SPACING + 10 * zoom
            if self.homologyChecked:
                drawAreaHeight -= self.NUM_GENE_ROWS * \
                    self.EACH_GENE_HEIGHT + 3 * self.BAND_SPACING
            if self.geneChecked:
                drawAreaHeight -= self.NUM_GENE_ROWS * \
                    self.EACH_GENE_HEIGHT + 3 * self.BAND_SPACING
        else:
            if self.selectedChr > -1:
                drawAreaHeight -= 20
            else:
                drawAreaHeight -= 30

# BEGIN HaplotypeAnalyst
        if self.haplotypeAnalystChecked and self.selectedChr > -1:
            drawAreaHeight -= self.EACH_GENE_HEIGHT * \
                (self.NR_INDIVIDUALS + 10) * 2 * zoom
# END HaplotypeAnalyst

        if zoom == 2:
            drawAreaHeight -= 60

        # Image map
        gifmap = HtmlGenWrapper.create_map_tag(name="WebQTLImageMap")

        newoffset = (xLeftOffset, xRightOffset, yTopOffset, yBottomOffset)
        # Draw the alternating-color background first and get plotXScale
        plotXScale = self.drawGraphBackground(
            canvas, gifmap, offset=newoffset, zoom=zoom, startMb=startMb, endMb=endMb)

        # draw bootstap
        if self.bootChecked and not self.multipleInterval:
            self.drawBootStrapResult(canvas, self.nboot, drawAreaHeight, plotXScale,
                                     offset=newoffset, zoom=zoom, startMb=startMb, endMb=endMb)

        # Draw clickable region and gene band if selected
        if self.plotScale == 'physic' and self.selectedChr > -1:
            self.drawClickBand(canvas, gifmap, plotXScale, offset=newoffset,
                               zoom=zoom, startMb=startMb, endMb=endMb)

            if self.geneChecked and self.geneCol:
                self.drawGeneBand(canvas, gifmap, plotXScale, offset=newoffset,
                                  zoom=zoom, startMb=startMb, endMb=endMb)
            if self.homologyChecked and self.homology:
                if self.geneChecked and self.geneCol:
                    yTopOffset = newoffset[3] + self.NUM_GENE_ROWS * \
                        self.EACH_GENE_HEIGHT + 3 * self.BAND_SPACING + 10
                self.drawHomologyBand(canvas, gifmap, plotXScale, offset=(xLeftOffset, xRightOffset, yTopOffset, yBottomOffset),
                                  zoom=zoom, startMb=startMb, endMb=endMb)
            if self.SNPChecked:
                self.drawSNPTrackNew(
                    canvas, offset=newoffset, zoom=2 * zoom, startMb=startMb, endMb=endMb)
# BEGIN HaplotypeAnalyst
            if self.haplotypeAnalystChecked:
                self.drawHaplotypeBand(
                    canvas, gifmap, plotXScale, offset=newoffset, zoom=zoom, startMb=startMb, endMb=endMb)
# END HaplotypeAnalyst
        # Draw X axis
        self.drawXAxis(canvas, drawAreaHeight, gifmap, plotXScale, showLocusForm,
                       offset=newoffset, zoom=zoom, startMb=startMb, endMb=endMb)
        # Draw QTL curve
        self.drawQTL(canvas, drawAreaHeight, gifmap, plotXScale,
                     offset=newoffset, zoom=zoom, startMb=startMb, endMb=endMb)

        # draw legend
        if self.multipleInterval:
            self.drawMultiTraitName(
                fd, canvas, gifmap, showLocusForm, offset=newoffset)
        elif self.legendChecked:
            self.drawLegendPanel(canvas, offset=newoffset, zoom=zoom)
        else:
            pass

        # draw position, no need to use a separate function
        if self.traitList[0].mb: # Only draw if position actually exists
            self.drawProbeSetPosition(
                canvas, plotXScale, offset=newoffset, zoom=zoom)

        return gifmap

    def drawBootStrapResult(self, canvas, nboot, drawAreaHeight, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
        im_drawer = ImageDraw.Draw(canvas)
        xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
        plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
        plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
        yZero = canvas.size[1] - yBottomOffset
        fontZoom = zoom
        if zoom == 2:
            fontZoom = 1.5

        bootHeightThresh = drawAreaHeight * 3 / 4

        # break bootstrap result into groups
        BootCoord = []
        i = 0
        previous_chr = None
        previous_chr_as_int = 0
        startX = xLeftOffset

        BootChrCoord = []
        if self.selectedChr == -1:  # ZS: If viewing full genome/all chromosomes
            for i, result in enumerate(self.qtlresults):
                if result['chr'] != previous_chr:
                    previous_chr = result['chr']
                    previous_chr_as_int += 1
                    if previous_chr_as_int != 1:
                        BootCoord.append(BootChrCoord)
                        BootChrCoord = []
                        startX += (
                            self.ChrLengthDistList[previous_chr_as_int - 2] + self.GraphInterval) * plotXScale
                if self.plotScale == 'physic':
                    Xc = startX + (result['Mb'] - self.startMb) * plotXScale
                else:
                    Xc = startX + \
                        (result['cM'] - self.qtlresults[0]['cM']) * plotXScale
                BootChrCoord.append([Xc, self.bootResult[i]])
        else:
            for i, result in enumerate(self.qtlresults):
                if str(result['chr']) == str(self.ChrList[self.selectedChr][0]):
                    if self.plotScale == 'physic':
                        Xc = startX + (result['Mb'] - \
                                       self.startMb) * plotXScale
                    else:
                        Xc = startX + \
                            (result['cM'] - self.qtlresults[0]
                             ['cM']) * plotXScale
                    BootChrCoord.append([Xc, self.bootResult[i]])
            BootCoord = [BootChrCoord]

        # reduce bootResult
        if self.selectedChr > -1:
            maxBootBar = 80.0
        else:
            maxBootBar = 200.0
        stepBootStrap = plotWidth / maxBootBar
        reducedBootCoord = []
        maxBootCount = 0

        for BootChrCoord in BootCoord:
            nBoot = len(BootChrCoord)
            bootStartPixX = BootChrCoord[0][0]
            bootCount = BootChrCoord[0][1]
            for i in range(1, nBoot):
                if BootChrCoord[i][0] - bootStartPixX < stepBootStrap:
                    bootCount += BootChrCoord[i][1]
                    continue
                else:
                    if maxBootCount < bootCount:
                        maxBootCount = bootCount
                    # end if
                    reducedBootCoord.append(
                        [bootStartPixX, BootChrCoord[i][0], bootCount])
                    bootStartPixX = BootChrCoord[i][0]
                    bootCount = BootChrCoord[i][1]
                # end else
            # end for
            # add last piece
            if BootChrCoord[-1][0] - bootStartPixX > stepBootStrap / 2.0:
                reducedBootCoord.append(
                    [bootStartPixX, BootChrCoord[-1][0], bootCount])
            else:
                reducedBootCoord[-1][2] += bootCount
                reducedBootCoord[-1][1] = BootChrCoord[-1][0]
            # end else
            if maxBootCount < reducedBootCoord[-1][2]:
                maxBootCount = reducedBootCoord[-1][2]
            # end if
        for item in reducedBootCoord:
            if item[2] > 0:
                if item[0] < xLeftOffset:
                    item[0] = xLeftOffset
                if item[0] > xLeftOffset + plotWidth:
                    item[0] = xLeftOffset + plotWidth
                if item[1] < xLeftOffset:
                    item[1] = xLeftOffset
                if item[1] > xLeftOffset + plotWidth:
                    item[1] = xLeftOffset + plotWidth
                if item[0] != item[1]:
                    im_drawer.rectangle(
                        xy=((item[0], yZero),
                            (item[1], yZero - item[2] * bootHeightThresh / maxBootCount)),
                        fill=self.BOOTSTRAP_BOX_COLOR, outline=BLACK)

        if maxBootCount == 0:
            return

        # draw boot scale
        highestPercent = (maxBootCount * 100.0) / nboot
        bootScale = Plot.detScale(0, highestPercent)
        bootScale = Plot.frange(
            bootScale[0], bootScale[1], bootScale[1] / bootScale[2])
        bootScale = bootScale[:-1] + [highestPercent]

        bootOffset = 50 * fontZoom
        bootScaleFont = ImageFont.truetype(
            font=VERDANA_FILE, size=13 * fontZoom)
        im_drawer.rectangle(
            xy=((canvas.size[0] - bootOffset, yZero - bootHeightThresh),
                (canvas.size[0] - bootOffset - 15 * zoom, yZero)),
            fill=YELLOW, outline=BLACK)
        im_drawer.line(
            xy=((canvas.size[0] - bootOffset + 4, yZero),
                (canvas.size[0] - bootOffset, yZero)),
            fill=BLACK)
        TEXT_Y_DISPLACEMENT = -8
        im_drawer.text(xy=(canvas.size[0] - bootOffset + 10, yZero + TEXT_Y_DISPLACEMENT), text='0%',
                       font=bootScaleFont, fill=BLACK)

        for item in bootScale:
            if item == 0:
                continue
            bootY = yZero - bootHeightThresh * item / highestPercent
            im_drawer.line(
                xy=((canvas.size[0] - bootOffset + 4, bootY),
                    (canvas.size[0] - bootOffset, bootY)),
                fill=BLACK)
            im_drawer.text(xy=(canvas.size[0] - bootOffset + 10, bootY + TEXT_Y_DISPLACEMENT),
                           text='%2.1f' % item, font=bootScaleFont, fill=BLACK)

        if self.legendChecked:
            if hasattr(self.traitList[0], 'chr') and hasattr(self.traitList[0], 'mb'):
                startPosY = 30
            else:
                startPosY = 15
            smallLabelFont = ImageFont.truetype(
                font=TREBUC_FILE, size=12 * fontZoom)
            leftOffset = canvas.size[0] - xRightOffset - 190
            im_drawer.rectangle(
                xy=((leftOffset, startPosY - 6),
                    (leftOffset + 12, startPosY + 6)),
                fill=YELLOW, outline=BLACK)
            im_drawer.text(xy=(canvas.size[0] - xRightOffset - 170, startPosY + TEXT_Y_DISPLACEMENT),
                           text='Frequency of the Peak LRS',
                           font=smallLabelFont, fill=BLACK)

    def drawProbeSetPosition(self, canvas, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
        im_drawer = ImageDraw.Draw(canvas)
        if len(self.traitList) != 1:
            return

        xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
        plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
        plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
        yZero = canvas.size[1] - yBottomOffset
        fontZoom = zoom
        if zoom == 2:
            fontZoom = 1.5

        try:
            Chr = self.traitList[0].chr
            Mb = self.traitList[0].mb
        except:
            return

        previous_chr = 1
        previous_chr_as_int = 0
        if self.plotScale == "physic":
            this_chr = str(self.ChrList[self.selectedChr][0])
        else:
            this_chr = str(self.ChrList[self.selectedChr][1] + 1)

        if self.plotScale == 'physic':
            if self.selectedChr > -1:
                if this_chr != Chr or Mb < self.startMb or Mb > self.endMb:
                    return
                else:
                    locPixel = xLeftOffset + (Mb - self.startMb) * plotXScale
            else:
                locPixel = xLeftOffset
                for i, _chr in enumerate(self.ChrList[1:]):
                    if _chr[0] != Chr:
                        locPixel += (self.ChrLengthDistList[i] + \
                                     self.GraphInterval) * plotXScale
                    else:
                        locPixel += Mb * plotXScale
                        break
        else:
            if self.selectedChr > -1:
                for i, qtlresult in enumerate(self.qtlresults):
                    if qtlresult['chr'] != self.selectedChr:
                        continue

                    if i == 0 and qtlresult['Mb'] >= Mb:
                        locPixel = -1
                        break

                    # the trait's position is between two traits
                    if i > 0 and self.qtlresults[i - 1]['Mb'] < Mb and qtlresult['Mb'] >= Mb:
                        locPixel = xLeftOffset + plotXScale * (self.qtlresults[i - 1]['Mb'] + (qtlresult['Mb'] - self.qtlresults[i - 1]['Mb']) * (
                            Mb - self.qtlresults[i - 1]['Mb']) / (qtlresult['Mb'] - self.qtlresults[i - 1]['Mb']))
                        break

                    # the trait's position is on the right of the last genotype
                    if i == len(self.qtlresults) and Mb >= qtlresult['Mb']:
                        locPixel = -1
            else:
                locPixel = xLeftOffset
                for i, _chr in enumerate(self.ChrList):
                    if i < (len(self.ChrList) - 1):
                        if _chr != Chr:
                            locPixel += (self.ChrLengthDistList[i] + \
                                         self.GraphInterval) * plotXScale
                        else:
                            locPixel += (Mb * (_chr[-1].cM - _chr[0].cM) / \
                                         self.ChrLengthCMList[i]) * plotXScale
                            break
        if locPixel >= 0 and self.plotScale == 'physic':
            traitPixel = ((locPixel, yZero), (locPixel - 7,
                                              yZero + 14), (locPixel + 7, yZero + 14))
            draw_open_polygon(canvas, xy=traitPixel, outline=BLACK,
                              fill=self.TRANSCRIPT_LOCATION_COLOR)

    def drawSNPTrackNew(self, canvas, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
        im_drawer = ImageDraw.Draw(canvas)
        if self.plotScale != 'physic' or self.selectedChr == -1 or not self.diffCol:
            return

        SNP_HEIGHT_MODIFIER = 18.0

        xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
        plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
        plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
        yZero = canvas.size[1] - yBottomOffset
        fontZoom = zoom
        if zoom == 2:
            fontZoom = 1.5

        drawSNPLocationY = yTopOffset + plotHeight
        #chrName = self.genotype[0].name
        chrName = self.ChrList[self.selectedChr][0]

        stepMb = (endMb - startMb) / plotWidth
        strainId1, strainId2 = self.diffCol
        SNPCounts = []

        while startMb < endMb:
            with database_connection(get_setting("SQL_URI")) as conn, conn.cursor() as cursor:
                # snp count
                cursor.execute("SELECT COUNT(*) FROM BXDSnpPosition "
                               "WHERE Chr = %s AND Mb >= %s AND Mb < %s AND "
                               "StrainId1 = %s AND StrainId2 = %s",
                               (chrName, f"{startMb:2.6f}",
                                f"{startMb + stepMb:2.6f}", strainId1, strainId2,))
                SNPCounts.append(cursor.fetchone()[0])
            startMb += stepMb

        if (len(SNPCounts) > 0):
            maxCount = max(SNPCounts)
            if maxCount > 0:
                for i in range(xLeftOffset, xLeftOffset + plotWidth):
                    snpDensity = float(
                        SNPCounts[i - xLeftOffset] * SNP_HEIGHT_MODIFIER / maxCount)
                    im_drawer.line(
                        xy=((i, drawSNPLocationY + (snpDensity) * zoom),
                            (i, drawSNPLocationY - (snpDensity) * zoom)),
                        fill=self.SNP_COLOR, width=1)

    def drawMultiTraitName(self, fd, canvas, gifmap, showLocusForm, offset=(40, 120, 80, 10), zoom=1):
        nameWidths = []
        yPaddingTop = 10
        colorFont = ImageFont.truetype(font=TREBUC_FILE, size=12)
        if len(self.qtlresults) > 20 and self.selectedChr > -1:
            rightShift = 20
            rightShiftStep = 60
            rectWidth = 10
        else:
            rightShift = 40
            rightShiftStep = 80
            rectWidth = 15

        for k, thisTrait in enumerate(self.traitList):
            thisLRSColor = self.colorCollection[k]
            kstep = k % 4
            if k != 0 and kstep == 0:
                if nameWidths:
                    rightShiftStep = max(nameWidths[-4:]) + rectWidth + 20
                rightShift += rightShiftStep

            name = thisTrait.displayName()
            nameWidth, nameHeight = im_drawer.textsize(name, font=colorFont)
            nameWidths.append(nameWidth)

            im_drawer.rectangle(
                xy=((rightShift, yPaddingTop + kstep * 15),
                    (rectWidth + rightShift, yPaddingTop + 10 + kstep * 15)),
                fill=thisLRSColor, outline=BLACK)
            im_drawer.text(
                text=name, xy=(rectWidth + 2 + rightShift,
                               yPaddingTop + 10 + kstep * 15),
                font=colorFont, fill=BLACK)
            if thisTrait.db:
                COORDS = "%d,%d,%d,%d" % (rectWidth + 2 + rightShift, yPaddingTop + kstep * \
                                          15, rectWidth + 2 + rightShift + nameWidth, yPaddingTop + 10 + kstep * 15,)
                HREF = "javascript:showDatabase3('%s','%s','%s','');" % (
                    showLocusForm, thisTrait.db.name, thisTrait.name)
                Areas = HtmlGenWrapper.create_area_tag(
                    shape='rect', coords=COORDS, href=HREF)
                gifmap.append(Areas)  # TODO

    def drawLegendPanel(self, canvas, offset=(40, 120, 80, 10), zoom=1):
        im_drawer = ImageDraw.Draw(canvas)
        xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
        plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
        plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
        yZero = canvas.size[1] - yBottomOffset
        TEXT_Y_DISPLACEMENT = -8
        fontZoom = zoom
        if zoom == 2:
            fontZoom = 1.5

        labelFont = ImageFont.truetype(font=TREBUC_FILE, size=12 * fontZoom)
        startPosY = 15
        stepPosY = 12 * fontZoom

        startPosX = canvas.size[0] - xRightOffset - 415
        if hasattr(self.traitList[0], 'chr') and hasattr(self.traitList[0], 'mb'):
            startPosY = 15
            nCol = 2
            smallLabelFont = ImageFont.truetype(
                font=TREBUC_FILE, size=12 * fontZoom)

            leftOffset = canvas.size[0] - xRightOffset - 190
            draw_open_polygon(
                canvas,
                xy=(
                    (leftOffset + 6, startPosY - 7),
                    (leftOffset - 1, startPosY + 7),
                    (leftOffset + 13, startPosY + 7)),
                outline=BLACK, fill=self.TRANSCRIPT_LOCATION_COLOR
            )
            TEXT_Y_DISPLACEMENT = -8
            im_drawer.text(
                text="Gene Location",
                xy=(leftOffset + 20, startPosY + TEXT_Y_DISPLACEMENT), font=smallLabelFont,
                fill=self.TOP_RIGHT_INFO_COLOR)

        if self.manhattan_plot != True:
            im_drawer.line(
                xy=((startPosX, startPosY), (startPosX + 32, startPosY)),
                fill=self.LRS_COLOR, width=2)
            im_drawer.text(
                text=self.LRS_LOD, xy=(
                    startPosX + 40, startPosY + TEXT_Y_DISPLACEMENT),
                font=labelFont, fill=BLACK)
            startPosY += stepPosY

        if self.additiveChecked:
            im_drawer.line(
                xy=((startPosX, startPosY), (startPosX + 17, startPosY)),
                fill=self.ADDITIVE_COLOR_POSITIVE, width=2)
            im_drawer.line(
                xy=((startPosX + 18, startPosY), (startPosX + 32, startPosY)),
                fill=self.ADDITIVE_COLOR_NEGATIVE, width=2)
            im_drawer.text(
                text='Additive Effect', xy=(startPosX + 40, startPosY + TEXT_Y_DISPLACEMENT),
                font=labelFont, fill=BLACK)
            startPosY += stepPosY

        if self.genotype.type == 'intercross' and self.dominanceChecked:
            im_drawer.line(
                xy=((startPosX, startPosY), (startPosX + 17, startPosY)),
                fill=self.DOMINANCE_COLOR_POSITIVE, width=4)
            im_drawer.line(
                xy=((startPosX + 18, startPosY), (startPosX + 35, startPosY)),
                fill=self.DOMINANCE_COLOR_NEGATIVE, width=4)
            im_drawer.text(
                text='Dominance Effect', xy=(startPosX + 42, startPosY + 5),
                font=labelFont, fill=BLACK)
            startPosY += stepPosY

        if self.haplotypeAnalystChecked:
            im_drawer.line(
                xy=((startPosX - 60, startPosY), (startPosX - 45, startPosY)),
                fill=self.HAPLOTYPE_NEGATIVE, width=8)
            im_drawer.line(
                xy=((startPosX - 45, startPosY), (startPosX - 30, startPosY)),
                fill=self.HAPLOTYPE_POSITIVE, width=8)
            im_drawer.line(
                xy=((startPosX - 30, startPosY), (startPosX - 15, startPosY)),
                fill=self.HAPLOTYPE_HETEROZYGOUS, width=8)
            im_drawer.line(
                xy=((startPosX - 15, startPosY), (startPosX, startPosY)),
                fill=self.HAPLOTYPE_RECOMBINATION, width=8)
            im_drawer.text(
                text='Haplotypes (Mat, Pat, Het, Unknown)',
                xy=(startPosX + 10, startPosY + TEXT_Y_DISPLACEMENT), font=labelFont, fill=BLACK)
            startPosY += stepPosY

        if self.permChecked and self.nperm > 0:
            thisStartX = startPosX
            if self.multipleInterval and not self.bootChecked:
                thisStartX = canvas.size[0] - xRightOffset - 205
            im_drawer.line(
                xy=((thisStartX, startPosY), (startPosX + 32, startPosY)),
                fill=self.SIGNIFICANT_COLOR, width=self.SIGNIFICANT_WIDTH)
            im_drawer.line(
                xy=((thisStartX, startPosY + stepPosY),
                    (startPosX + 32, startPosY + stepPosY)),
                fill=self.SUGGESTIVE_COLOR, width=self.SUGGESTIVE_WIDTH)
            im_drawer.text(
                text='Significant %s = %2.2f' % (
                    self.LRS_LOD, self.significant),
                xy=(thisStartX + 40, startPosY + TEXT_Y_DISPLACEMENT), font=labelFont, fill=BLACK)
            im_drawer.text(
                text='Suggestive %s = %2.2f' % (self.LRS_LOD, self.suggestive),
                xy=(thisStartX + 40, startPosY + TEXT_Y_DISPLACEMENT + stepPosY), font=labelFont,
                fill=BLACK)

        labelFont = ImageFont.truetype(font=VERDANA_FILE, size=12 * fontZoom)
        labelColor = BLACK

        if self.dataset.type == "Publish" or self.dataset.type == "Geno":
            dataset_label = self.dataset.fullname
        else:
            dataset_label = "%s - %s" % (self.dataset.group.name,
                                         self.dataset.fullname)


        self.current_datetime = datetime.datetime.now().strftime("%b %d %Y %H:%M:%S")
        string1 = 'UTC Timestamp: %s' % (self.current_datetime)
        string2 = 'Dataset: %s' % (dataset_label)
        string3 = 'Trait Hash: %s' % (self.vals_hash)

        if self.genofile_string == "":
            string4 = 'Genotype File: %s.geno' % self.dataset.group.name
        else:
            string4 = 'Genotype File: %s' % self.genofile_string.split(":")[1]

        string6 = ''
        if self.mapping_method == "gemma" or self.mapping_method == "gemma_bimbam":
            if self.use_loco == "True":
                string5 = 'Using GEMMA mapping method with LOCO and '
            else:
                string5 = 'Using GEMMA mapping method with '
            if self.covariates != "":
                string5 += 'the cofactors below:'
                cofactor_names = ", ".join(
                    [covar.split(":")[0] for covar in self.covariates.split(",")])
                string6 = cofactor_names
            else:
                string5 += 'no cofactors'
        elif self.mapping_method == "rqtl_plink" or self.mapping_method == "rqtl_geno":
            string5 = 'Using R/qtl mapping method with '
            if self.covariates != "":
                string5 += 'the cofactors below:'
                cofactor_names = ", ".join(
                    [covar.split(":")[0] for covar in self.covariates.split(",")])
                string6 = cofactor_names
            elif self.controlLocus and self.doControl != "false":
                string5 += '%s as control' % self.controlLocus
            else:
                string5 += 'no cofactors'
        else:
            string5 = 'Using Haldane mapping function with '
            if self.controlLocus and self.doControl != "false":
                string5 += '%s as control' % self.controlLocus
            else:
                string5 += 'no control for other QTLs'

        y_constant = 10
        if self.this_trait.name:
            if self.selectedChr == -1:
                identification = "Mapping on All Chromosomes for "
            else:
                identification = "Mapping on Chromosome %s for " % (
                    self.ChrList[self.selectedChr][0])

            if self.this_trait.symbol:
                identification += "Trait: %s - %s" % (
                    self.this_trait.display_name, self.this_trait.symbol)
            elif self.dataset.type == "Publish":
                if self.this_trait.post_publication_abbreviation:
                    identification += "Trait: %s - %s" % (
                        self.this_trait.display_name, self.this_trait.post_publication_abbreviation)
                elif self.this_trait.pre_publication_abbreviation:
                    identification += "Trait: %s - %s" % (
                        self.this_trait.display_name, self.this_trait.pre_publication_abbreviation)
                else:
                    identification += "Trait: %s" % (self.this_trait.display_name)
            else:
                identification += "Trait: %s" % (self.this_trait.display_name)
            identification += " with %s samples" % (self.n_samples)

            d = 4 + max(
                im_drawer.textsize(identification, font=labelFont)[0],
                im_drawer.textsize(string1, font=labelFont)[0],
                im_drawer.textsize(string2, font=labelFont)[0],
                im_drawer.textsize(string3, font=labelFont)[0],
                im_drawer.textsize(string4, font=labelFont)[0])
            im_drawer.text(
                text=identification,
                xy=(xLeftOffset, y_constant * fontZoom), font=labelFont,
                fill=labelColor)
            y_constant += 15
        else:
            d = 4 + max(
                im_drawer.textsize(string1, font=labelFont)[0],
                im_drawer.textsize(string2, font=labelFont)[0],
                im_drawer.textsize(string3, font=labelFont)[0],
                im_drawer.textsize(string4, font=labelFont)[0])

        if len(self.transform) > 0:
            transform_text = "Transform - "
            if self.transform == "qnorm":
                transform_text += "Quantile Normalized"
            elif self.transform == "log2" or self.transform == "log10":
                transform_text += self.transform.capitalize()
            elif self.transform == "sqrt":
                transform_text += "Square Root"
            elif self.transform == "zscore":
                transform_text += "Z-Score"
            elif self.transform == "invert":
                transform_text += "Invert +/-"

            im_drawer.text(
                text=transform_text, xy=(xLeftOffset, y_constant * fontZoom),
                font=labelFont, fill=labelColor)
            y_constant += 15
        im_drawer.text(
            text=string1, xy=(xLeftOffset, y_constant * fontZoom),
            font=labelFont, fill=labelColor)
        y_constant += 15
        im_drawer.text(
            text=string2, xy=(xLeftOffset, y_constant * fontZoom),
            font=labelFont, fill=labelColor)
        y_constant += 15
        im_drawer.text(
            text=string3, xy=(xLeftOffset, y_constant * fontZoom),
            font=labelFont, fill=labelColor)
        y_constant += 15
        im_drawer.text(
            text=string4, xy=(xLeftOffset, y_constant * fontZoom),
            font=labelFont, fill=labelColor)
        y_constant += 15
        if string4 != '':
            im_drawer.text(
                text=string5, xy=(xLeftOffset, y_constant * fontZoom),
                font=labelFont, fill=labelColor)
            y_constant += 15
            if string5 != '':
                im_drawer.text(
                    text=string6, xy=(xLeftOffset, y_constant * fontZoom),
                    font=labelFont, fill=labelColor)

    def drawHomologyBand(self, canvas, gifmap, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
        im_drawer = ImageDraw.Draw(canvas)
        if self.plotScale != 'physic' or self.selectedChr == -1 or not self.homology:
            return

        xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
        plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
        plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
        yZero = canvas.size[1] - yBottomOffset
        fontZoom = zoom
        if zoom == 2:
            fontZoom = 1.5

        yPaddingTop = yTopOffset

        for index, homology_dict in enumerate(self.homology):
            ref_strand = homology_dict["ref_strand"]
            ref_start = homology_dict["ref_start"]
            ref_end = homology_dict["ref_end"]
            query_chr = homology_dict["query_chr"]
            query_strand = homology_dict["query_strand"]
            query_start = homology_dict["query_start"]
            query_end = homology_dict["query_end"]

            geneLength = (ref_end - ref_start) * 1000.0
            tenPercentLength = geneLength * 0.0001

            geneStartPix = xLeftOffset + \
                plotXScale * (float(ref_start) - startMb)
            geneEndPix = xLeftOffset + plotXScale * \
                (float(ref_end) - startMb)  # at least one pixel

            if (geneEndPix < xLeftOffset):
                return  # this gene is not on the screen
            if (geneEndPix > xLeftOffset + plotWidth):
                geneEndPix = xLeftOffset + plotWidth  # clip the last in-range gene
            if (geneStartPix > xLeftOffset + plotWidth):
                return  # we are outside the valid on-screen range, so stop drawing genes
            elif (geneStartPix < xLeftOffset):
                geneStartPix = xLeftOffset  # clip the first in-range gene

            myColor = BLACK

            outlineColor = myColor
            fillColor = myColor

            TITLE = f"hg38: Chr {query_chr} from {query_start:.3f} to {query_end:.3f} Mb"
            HREF = f"http://genome.ucsc.edu/cgi-bin/hgTracks?db=hg38&position=chr{query_chr}:{int(query_start * 1000000)}-{int(query_end * 1000000)}"

            # Draw Genes
            geneYLocation = yPaddingTop + \
                (index % self.NUM_GENE_ROWS) * self.EACH_GENE_HEIGHT * zoom
            if self.dataset.group.species == "mouse" or self.dataset.group.species == "rat":
                geneYLocation += 4 * self.BAND_HEIGHT + 4 * self.BAND_SPACING
            else:
                geneYLocation += 3 * self.BAND_HEIGHT + 3 * self.BAND_SPACING

            # draw the detail view
            utrColor = ImageColor.getrgb("rgb(66%, 66%, 66%)")
            arrowColor = ImageColor.getrgb("rgb(70%, 70%, 70%)")

            # draw the line that runs the entire length of the gene
            im_drawer.line(
                xy=(
                    (geneStartPix, geneYLocation + \
                        self.EACH_GENE_HEIGHT / 2 * zoom),
                    (geneEndPix, geneYLocation + self.EACH_GENE_HEIGHT / 2 * zoom)),
                fill=outlineColor, width=1)

            # draw the arrows
            if geneEndPix - geneStartPix < 1:
                genePixRange = 1
            else:
                genePixRange = int(geneEndPix - geneStartPix)
            for xCoord in range(0, genePixRange):

                if (xCoord % self.EACH_GENE_ARROW_SPACING == 0 and xCoord + self.EACH_GENE_ARROW_SPACING < geneEndPix - geneStartPix) or xCoord == 0:
                    if query_strand == "+":
                        im_drawer.line(
                            xy=((geneStartPix + xCoord, geneYLocation),
                                (geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
                                    geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
                            fill=arrowColor, width=1)
                        im_drawer.line(
                            xy=((geneStartPix + xCoord,
                                    geneYLocation + self.EACH_GENE_HEIGHT * zoom),
                                (geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
                                    geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
                            fill=arrowColor, width=1)
                    else:
                        im_drawer.line(
                            xy=((geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
                                    geneYLocation),
                                (geneStartPix + xCoord,
                                    geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
                            fill=arrowColor, width=1)
                        im_drawer.line(
                            xy=((geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
                                    geneYLocation + self.EACH_GENE_HEIGHT * zoom),
                                (geneStartPix + xCoord,
                                    geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
                            fill=arrowColor, width=1)

            COORDS = "%d, %d, %d, %d" % (
                geneStartPix, geneYLocation, geneEndPix, (geneYLocation + self.EACH_GENE_HEIGHT))

            gifmap.append(
                HtmlGenWrapper.create_area_tag(
                    shape='rect',
                    coords=COORDS,
                    href=HREF,
                    title=TITLE,
                    target="_blank"))

    def drawGeneBand(self, canvas, gifmap, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
        im_drawer = ImageDraw.Draw(canvas)
        if self.plotScale != 'physic' or self.selectedChr == -1 or not self.geneCol:
            return

        xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
        plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
        plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
        yZero = canvas.size[1] - yBottomOffset
        fontZoom = zoom
        if zoom == 2:
            fontZoom = 1.5

        yPaddingTop = yTopOffset

        for gIndex, theGO in enumerate(self.geneCol):
            geneNCBILink = 'http://www.ncbi.nlm.nih.gov/gene?term=%s'
            if self.dataset.group.species == "mouse":
                exonStarts = []
                exonEnds = []
                txStart = theGO["TxStart"]
                txEnd = theGO["TxEnd"]
                geneLength = (txEnd - txStart) * 1000.0
                tenPercentLength = geneLength * 0.0001
                SNPdensity = theGO["snpCount"] / geneLength

                if theGO['exonStarts']:
                    exonStarts = list(
                        map(float, theGO['exonStarts'].split(",")[:-1]))
                    exonEnds = list(map(float, theGO['exonEnds'].split(",")[:-1]))
                cdsStart = theGO['cdsStart']
                cdsEnd = theGO['cdsEnd']
                accession = theGO['NM_ID']
                geneSymbol = theGO["GeneSymbol"]
                strand = theGO["Strand"]
                exonCount = theGO["exonCount"]

                geneStartPix = xLeftOffset + \
                    plotXScale * (float(txStart) - startMb)
                geneEndPix = xLeftOffset + plotXScale * \
                    (float(txEnd) - startMb)  # at least one pixel

                if (geneEndPix < xLeftOffset):
                    return  # this gene is not on the screen
                elif (geneEndPix > xLeftOffset + plotWidth):
                    geneEndPix = xLeftOffset + plotWidth  # clip the last in-range gene
                if (geneStartPix > xLeftOffset + plotWidth):
                    return  # we are outside the valid on-screen range, so stop drawing genes
                elif (geneStartPix < xLeftOffset):
                    geneStartPix = xLeftOffset  # clip the first in-range gene

                # color the gene based on SNP density
                # found earlier, needs to be recomputed as snps are added
                # always apply colors now, even if SNP Track not checked - Zach 11/24/2010

                densities = [1.0000000000000001e-05, 0.094094033555233408,
                             0.3306166377816987, 0.88246026851027781, 2.6690084029581951, 4.1, 61.0]
                if SNPdensity < densities[0]:
                    myColor = BLACK
                elif SNPdensity < densities[1]:
                    myColor = PURPLE
                elif SNPdensity < densities[2]:
                    myColor = DARKBLUE
                elif SNPdensity < densities[3]:
                    myColor = DARKGREEN
                elif SNPdensity < densities[4]:
                    myColor = GOLD
                elif SNPdensity < densities[5]:
                    myColor = DARKORANGE
                else:
                    myColor = DARKRED

                outlineColor = myColor
                fillColor = myColor

                TITLE = "Gene: %s (%s)\nFrom %2.3f to %2.3f Mb (%s)\nNum. exons: %d." % (
                    geneSymbol, accession, float(txStart), float(txEnd), strand, exonCount)
                # NL: 06-02-2011 Rob required to change this link for gene related
                HREF = geneNCBILink % geneSymbol

            elif self.dataset.group.species == "rat":
                exonStarts = []
                exonEnds = []
                txStart = theGO["TxStart"]
                txEnd = theGO["TxEnd"]
                cdsStart = theGO["TxStart"]
                cdsEnd = theGO["TxEnd"]
                geneSymbol = theGO["GeneSymbol"]
                strand = theGO["Strand"]
                exonCount = 0

                geneStartPix = xLeftOffset + \
                    plotXScale * (float(txStart) - startMb)
                geneEndPix = xLeftOffset + plotXScale * \
                    (float(txEnd) - startMb)  # at least one pixel

                if (geneEndPix < xLeftOffset):
                    return  # this gene is not on the screen
                elif (geneEndPix > xLeftOffset + plotWidth):
                    geneEndPix = xLeftOffset + plotWidth  # clip the last in-range gene
                if (geneStartPix > xLeftOffset + plotWidth):
                    return  # we are outside the valid on-screen range, so stop drawing genes
                elif (geneStartPix < xLeftOffset):
                    geneStartPix = xLeftOffset  # clip the first in-range gene

                outlineColor = DARKBLUE
                fillColor = DARKBLUE
                TITLE = "Gene: %s\nFrom %2.3f to %2.3f Mb (%s)" % (
                    geneSymbol, float(txStart), float(txEnd), strand)
                # NL: 06-02-2011 Rob required to change this link for gene related
                HREF = geneNCBILink % geneSymbol
            else:
                outlineColor = ORANGE
                fillColor = ORANGE
                TITLE = "Gene: %s" % geneSymbol

            # Draw Genes
            geneYLocation = yPaddingTop + \
                (gIndex % self.NUM_GENE_ROWS) * self.EACH_GENE_HEIGHT * zoom
            if self.dataset.group.species == "mouse" or self.dataset.group.species == "rat":
                geneYLocation += 4 * self.BAND_HEIGHT + 4 * self.BAND_SPACING
            else:
                geneYLocation += 3 * self.BAND_HEIGHT + 3 * self.BAND_SPACING

            # draw the detail view
            if self.endMb - self.startMb <= self.DRAW_DETAIL_MB and geneEndPix - geneStartPix > self.EACH_GENE_ARROW_SPACING * 3:
                utrColor = ImageColor.getrgb("rgb(66%, 66%, 66%)")
                arrowColor = ImageColor.getrgb("rgb(70%, 70%, 70%)")

                # draw the line that runs the entire length of the gene
                im_drawer.line(
                    xy=(
                        (geneStartPix, geneYLocation + \
                         self.EACH_GENE_HEIGHT / 2 * zoom),
                        (geneEndPix, geneYLocation + self.EACH_GENE_HEIGHT / 2 * zoom)),
                    fill=outlineColor, width=1)

                # draw the arrows
                if geneEndPix - geneStartPix < 1:
                    genePixRange = 1
                else:
                    genePixRange = int(geneEndPix - geneStartPix)
                for xCoord in range(0, genePixRange):

                    if (xCoord % self.EACH_GENE_ARROW_SPACING == 0 and xCoord + self.EACH_GENE_ARROW_SPACING < geneEndPix - geneStartPix) or xCoord == 0:
                        if strand == "+":
                            im_drawer.line(
                                xy=((geneStartPix + xCoord, geneYLocation),
                                    (geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
                                     geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
                                fill=arrowColor, width=1)
                            im_drawer.line(
                                xy=((geneStartPix + xCoord,
                                     geneYLocation + self.EACH_GENE_HEIGHT * zoom),
                                    (geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
                                     geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
                                fill=arrowColor, width=1)
                        else:
                            im_drawer.line(
                                xy=((geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
                                     geneYLocation),
                                    (geneStartPix + xCoord,
                                     geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
                                fill=arrowColor, width=1)
                            im_drawer.line(
                                xy=((geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
                                     geneYLocation + self.EACH_GENE_HEIGHT * zoom),
                                    (geneStartPix + xCoord,
                                     geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
                                fill=arrowColor, width=1)

                # draw the blocks for the exon regions
                for i in range(0, len(exonStarts)):
                    exonStartPix = (
                        exonStarts[i] - startMb) * plotXScale + xLeftOffset
                    exonEndPix = (exonEnds[i] - startMb) * \
                        plotXScale + xLeftOffset
                    if (exonStartPix < xLeftOffset):
                        exonStartPix = xLeftOffset
                    if (exonEndPix < xLeftOffset):
                        exonEndPix = xLeftOffset
                    if (exonEndPix > xLeftOffset + plotWidth):
                        exonEndPix = xLeftOffset + plotWidth
                    if (exonStartPix > xLeftOffset + plotWidth):
                        exonStartPix = xLeftOffset + plotWidth
                    im_drawer.rectangle(
                        xy=((exonStartPix, geneYLocation),
                            (exonEndPix, (geneYLocation + self.EACH_GENE_HEIGHT * zoom))),
                        outline=outlineColor, fill=fillColor)

                # draw gray blocks for 3' and 5' UTR blocks
                if cdsStart and cdsEnd:
                    utrStartPix = (txStart - startMb) * \
                        plotXScale + xLeftOffset
                    utrEndPix = (cdsStart - startMb) * plotXScale + xLeftOffset
                    if (utrStartPix < xLeftOffset):
                        utrStartPix = xLeftOffset
                    if (utrEndPix < xLeftOffset):
                        utrEndPix = xLeftOffset
                    if (utrEndPix > xLeftOffset + plotWidth):
                        utrEndPix = xLeftOffset + plotWidth
                    if (utrStartPix > xLeftOffset + plotWidth):
                        utrStartPix = xLeftOffset + plotWidth

                    if self.endMb - self.startMb <= self.DRAW_UTR_LABELS_MB:
                        if strand == "-":
                            labelText = "3'"
                        else:
                            labelText = "5'"
                        im_drawer.text(
                            text=labelText,
                            xy=(utrStartPix - 9, geneYLocation + \
                                self.EACH_GENE_HEIGHT),
                            font=ImageFont.truetype(font=ARIAL_FILE, size=2))

                    # the second UTR region

                    utrStartPix = (cdsEnd - startMb) * plotXScale + xLeftOffset
                    utrEndPix = (txEnd - startMb) * plotXScale + xLeftOffset
                    if (utrStartPix < xLeftOffset):
                        utrStartPix = xLeftOffset
                    if (utrEndPix < xLeftOffset):
                        utrEndPix = xLeftOffset
                    if (utrEndPix > xLeftOffset + plotWidth):
                        utrEndPix = xLeftOffset + plotWidth
                    if (utrStartPix > xLeftOffset + plotWidth):
                        utrStartPix = xLeftOffset + plotWidth

                    if self.endMb - self.startMb <= self.DRAW_UTR_LABELS_MB:
                        if strand == "-":
                            labelText = "5'"
                        else:
                            labelText = "3'"
                            im_drawer.text(
                                text=labelText,
                                xy=(utrEndPix + 2, geneYLocation + \
                                    self.EACH_GENE_HEIGHT),
                                font=ImageFont.truetype(font=ARIAL_FILE, size=2))

            # draw the genes as rectangles
            else:
                im_drawer.rectangle(
                    xy=((geneStartPix, geneYLocation),
                        (geneEndPix, (geneYLocation + self.EACH_GENE_HEIGHT * zoom))),
                    outline=outlineColor, fill=fillColor)

            COORDS = "%d, %d, %d, %d" % (
                geneStartPix, geneYLocation, geneEndPix, (geneYLocation + self.EACH_GENE_HEIGHT))
            # NL: 06-02-2011 Rob required to display NCBI info in a new window
            gifmap.append(
                HtmlGenWrapper.create_area_tag(
                    shape='rect',
                    coords=COORDS,
                    href=HREF,
                    title=TITLE,
                    target="_blank"))

# BEGIN HaplotypeAnalyst
    def drawHaplotypeBand(self, canvas, gifmap, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
        if self.plotScale != 'physic' or self.selectedChr == -1 or not self.geneCol:
            return

        xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
        plotWidth = canvas.size[0] - xLeftOffset - xRightOffset

        yPaddingTop = yTopOffset

        thisTrait = self.this_trait

        samplelist = list(self.genotype.prgy)

        smd = []
        for sample in self.sample_vals_dict.keys():
            if self.sample_vals_dict[sample] != "x" and sample in samplelist:
                temp = GeneralObject(name=sample, value=float(
                    self.sample_vals_dict[sample]))
                smd.append(temp)
            else:
                continue

        smd.sort(key=lambda A: A.value)
        smd.reverse()

        oldgeneEndPix = -1
        # Initializing plotRight, error before
        plotRight = xRightOffset

        im_drawer = ImageDraw.Draw(canvas)

# find out PlotRight
        for _chr in self.genotype:
            if _chr.name == self.ChrList[self.selectedChr][0]:
                for i, _locus in enumerate(_chr):
                    txStart = _chr[i].Mb
                    txEnd = _chr[i].Mb

                    geneStartPix = xLeftOffset + plotXScale * \
                        (float(txStart) - startMb) - 0
                    geneEndPix = xLeftOffset + plotXScale * \
                        (float(txEnd) - startMb) - 0

                    drawit = 1
                    if (geneStartPix < xLeftOffset):
                        drawit = 0
                    if (geneStartPix > xLeftOffset + plotWidth):
                        drawit = 0

                    if drawit == 1:
                        if _chr[i].name != " - ":
                            plotRight = geneEndPix + 4

# end find out PlotRight

        firstGene = 1
        lastGene = 0

        # Sets the length to the length of the strain list. Beforehand, "oldgeno = self.genotype[0][i].genotype"
        # was the only place it was initialized, which worked as long as the very start (startMb = None/0) wasn't being mapped.
        # Now there should always be some value set for "oldgeno" - Zach 12/14/2010
        oldgeno = [None] * len(self.strainlist)

        for i, _chr in enumerate(self.genotype):
            if _chr.name == self.ChrList[self.selectedChr][0]:
                for j, _locus in enumerate(_chr):
                    txStart = _chr[j].Mb
                    txEnd = _chr[j].Mb

                    geneStartPix = xLeftOffset + plotXScale * \
                        (float(txStart) - startMb) - 0
                    geneEndPix = xLeftOffset + plotXScale * \
                        (float(txEnd) - startMb) + 0

                    if oldgeneEndPix >= xLeftOffset:
                        drawStart = oldgeneEndPix + 4
                    else:
                        drawStart = xLeftOffset + 3

                    drawEnd = plotRight - 9

                    drawit = 1

                    if (geneStartPix < xLeftOffset):
                        if firstGene == 1:
                            drawit = 1
                        else:
                            drawit = 0

                    elif (geneStartPix > (xLeftOffset + plotWidth - 3)):
                        if lastGene == 0:
                            drawit = 1
                            drawEnd = xLeftOffset + plotWidth - 6
                            lastGene = 1
                        else:
                            break

                    else:
                        firstGene = 0
                        drawit = 1

                    if drawit == 1:
                        myColor = DARKBLUE
                        outlineColor = myColor
                        fillColor = myColor

                        maxind = 0

                        # Draw Genes

                        geneYLocation = yPaddingTop
                        if (self.geneChecked and self.geneCol):
                            geneYLocation += self.NUM_GENE_ROWS * self.EACH_GENE_HEIGHT * zoom
                        if (self.homologyChecked and self.homology):
                            geneYLocation += self.NUM_GENE_ROWS * self.EACH_GENE_HEIGHT * zoom
                        if self.dataset.group.species == "mouse" or self.dataset.group.species == "rat":
                            geneYLocation += 4 * self.BAND_HEIGHT + 4 * self.BAND_SPACING
                        else:
                            geneYLocation += 3 * self.BAND_HEIGHT + 3 * self.BAND_SPACING

                        if _chr[j].name != " - ":

                            if (firstGene == 1) and (lastGene != 1):
                                oldgeneEndPix = drawStart = xLeftOffset
                                oldgeno = _chr[j].genotype
                                continue

                            for k, _geno in enumerate(_chr[j].genotype):
                                plotbxd = 0
                                if samplelist[k] in [item.name for item in smd]:
                                    plotbxd = 1

                                if (plotbxd == 1):
                                    ind = 0
                                    if samplelist[k] in [item.name for item in smd]:
                                        ind = [item.name for item in smd].index(
                                            samplelist[k])

                                    maxind = max(ind, maxind)

                                    # lines
                                    if (oldgeno[k] == -1 and _geno == -1):
                                        mylineColor = self.HAPLOTYPE_NEGATIVE
                                    elif (oldgeno[k] == 1 and _geno == 1):
                                        mylineColor = self.HAPLOTYPE_POSITIVE
                                    elif (oldgeno[k] == 0 and _geno == 0):
                                        mylineColor = self.HAPLOTYPE_HETEROZYGOUS
                                    else:
                                        mylineColor = self.HAPLOTYPE_RECOMBINATION  # XZ: Unknown

                                    im_drawer.line(
                                        xy=((drawStart,
                                             geneYLocation + 7 + 2 * ind * self.EACH_GENE_HEIGHT * zoom),
                                            (drawEnd,
                                             geneYLocation + 7 + 2 * ind * self.EACH_GENE_HEIGHT * zoom)),
                                        fill=mylineColor, width=zoom * (self.EACH_GENE_HEIGHT + 2))

                                    fillColor = BLACK
                                    outlineColor = BLACK
                                    if lastGene == 0:
                                        im_drawer.rectangle(
                                            xy=((geneStartPix,
                                                 geneYLocation + 2 * ind * self.EACH_GENE_HEIGHT * zoom),
                                                (geneEndPix,
                                                 geneYLocation + 2 * ind * self.EACH_GENE_HEIGHT + 2 * self.EACH_GENE_HEIGHT * zoom)),
                                            outline=outlineColor, fill=fillColor)

                                    COORDS = "%d, %d, %d, %d" % (
                                        geneStartPix, geneYLocation + ind * self.EACH_GENE_HEIGHT, geneEndPix + 1, (geneYLocation + ind * self.EACH_GENE_HEIGHT))
                                    TITLE = "Strain: %s, marker (%s) \n Position  %2.3f Mb." % (
                                        samplelist[k], _chr[j].name, float(txStart))
                                    HREF = ''
                                    gifmap.append(
                                        HtmlGenWrapper.create_area_tag(
                                            shape='rect',
                                            coords=COORDS,
                                            href=HREF,
                                            title=TITLE))

                                    # if there are no more markers in a chromosome, the plotRight value calculated above will be before the plotWidth
                                    # resulting in some empty space on the right side of the plot area. This draws an "unknown" bar from plotRight to the edge.
                                    if (plotRight < (xLeftOffset + plotWidth - 3)) and (lastGene == 0):
                                        drawEnd = xLeftOffset + plotWidth - 6
                                        mylineColor = self.HAPLOTYPE_RECOMBINATION
                                    im_drawer.line(
                                        xy=((plotRight,
                                             geneYLocation + 7 + 2 * ind * self.EACH_GENE_HEIGHT * zoom),
                                            (drawEnd,
                                             geneYLocation + 7 + 2 * ind * self.EACH_GENE_HEIGHT * zoom)),
                                        fill=mylineColor, width=zoom * (self.EACH_GENE_HEIGHT + 2))

                            if lastGene == 0:
                                draw_rotated_text(
                                    canvas, text="%s" % (_chr[j].name),
                                    font=ImageFont.truetype(font=VERDANA_FILE,
                                                            size=12),
                                    xy=(geneStartPix,
                                        geneYLocation + 17 + 2 * maxind * self.EACH_GENE_HEIGHT * zoom),
                                    fill=BLACK, angle=-90)

                            oldgeneEndPix = geneEndPix
                            oldgeno = _chr[j].genotype
                            firstGene = 0
                        else:
                            lastGene = 0

        for i, _chr in enumerate(self.genotype):
            if _chr.name == self.ChrList[self.selectedChr][0]:
                for j, _geno in enumerate(_chr[1].genotype):

                    plotbxd = 0
                    if samplelist[j] in [item.name for item in smd]:
                        plotbxd = 1

                    if (plotbxd == 1):
                        ind = [item.name for item in smd].index(
                            samplelist[j]) - 1
                        expr = smd[ind+1].value

                        # Place where font is hardcoded
                        im_drawer.text(
                            text="%s" % (samplelist[j]),
                            xy=((xLeftOffset + plotWidth + 10),
                                geneYLocation + 11 + 2 * ind * self.EACH_GENE_HEIGHT * zoom),
                            font=ImageFont.truetype(
                                font=VERDANA_FILE, size=12),
                            fill=BLACK)
                        im_drawer.text(
                            text="%2.2f" % (expr),
                            xy=((xLeftOffset + plotWidth + 80),
                                geneYLocation + 11 + 2 * ind * self.EACH_GENE_HEIGHT * zoom),
                            font=ImageFont.truetype(
                                font=VERDANA_FILE, size=12),
                            fill=BLACK)

# END HaplotypeAnalyst

    def drawClickBand(self, canvas, gifmap, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
        im_drawer = ImageDraw.Draw(canvas)
        if self.plotScale != 'physic' or self.selectedChr == -1:
            return

        xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
        plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
        plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
        yZero = canvas.size[1] - yBottomOffset
        fontZoom = zoom
        if zoom == 2:
            fontZoom = 1.5

        # only draw this many clickable regions (if you set it higher, you get more precision in clicking,
        # but it makes the HTML huge, and takes forever to render the page in the first place)
        # Draw the bands that you can click on to go to UCSC / Ensembl
        MAX_CLICKABLE_REGION_DIVISIONS = 100
        clickableRegionLabelFont = ImageFont.truetype(
            font=VERDANA_FILE, size=9)
        pixelStep = max(
            5, int(float(plotWidth) / MAX_CLICKABLE_REGION_DIVISIONS))
        # pixelStep: every N pixels, we make a new clickable area for the user to go to that area of the genome.

        numBasesCurrentlyOnScreen = self.kONE_MILLION * \
            abs(startMb - endMb)  # Number of bases on screen now
        flankingWidthInBases = int(
            min((float(numBasesCurrentlyOnScreen) / 2.0), (5 * self.kONE_MILLION)))
        webqtlZoomWidth = numBasesCurrentlyOnScreen / 16.0
        # Flanking width should be such that we either zoom in to a 10 million base region, or we show the clicked region at the same scale as we are currently seeing.

        currentChromosome = self.genotype[0].name
        i = 0

        paddingTop = yTopOffset
        phenogenPaddingTop = paddingTop + \
            (self.BAND_HEIGHT + self.BAND_SPACING)
        if self.dataset.group.species == "mouse" or self.dataset.group.species == "rat":
            ucscPaddingTop = paddingTop + 2 * \
                (self.BAND_HEIGHT + self.BAND_SPACING)
            ensemblPaddingTop = paddingTop + 3 * \
                (self.BAND_HEIGHT + self.BAND_SPACING)
        else:
            ucscPaddingTop = paddingTop + \
                (self.BAND_HEIGHT + self.BAND_SPACING)
            ensemblPaddingTop = paddingTop + 2 * \
                (self.BAND_HEIGHT + self.BAND_SPACING)

        if zoom == 1:
            for pixel in range(xLeftOffset, xLeftOffset + plotWidth, pixelStep):

                calBase = self.kONE_MILLION * \
                    (startMb + (endMb - startMb) * \
                     (pixel - xLeftOffset - 0.0) / plotWidth)

                xBrowse1 = pixel
                xBrowse2 = min(xLeftOffset + plotWidth,
                               (pixel + pixelStep - 1))

                WEBQTL_COORDS = "%d, %d, %d, %d" % (
                    xBrowse1, paddingTop, xBrowse2, (paddingTop + self.BAND_HEIGHT))
                WEBQTL_HREF = "javascript:rangeView('%s', %f, %f)" % (self.selectedChr - 1, max(
                    0, (calBase - webqtlZoomWidth)) / 1000000.0, (calBase + webqtlZoomWidth) / 1000000.0)

                WEBQTL_TITLE = "Click to view this section of the genome in WebQTL"
                gifmap.append(
                    HtmlGenWrapper.create_area_tag(
                        shape='rect',
                        coords=WEBQTL_COORDS,
                        href=WEBQTL_HREF,
                        title=WEBQTL_TITLE))
                im_drawer.rectangle(
                    xy=((xBrowse1, paddingTop),
                        (xBrowse2, (paddingTop + self.BAND_HEIGHT))),
                    outline=self.CLICKABLE_WEBQTL_REGION_COLOR,
                    fill=self.CLICKABLE_WEBQTL_REGION_COLOR)
                im_drawer.line(
                    xy=((xBrowse1, paddingTop), (xBrowse1,
                                                 (paddingTop + self.BAND_HEIGHT))),
                    fill=self.CLICKABLE_WEBQTL_REGION_OUTLINE_COLOR)

                if self.dataset.group.species == "mouse" or self.dataset.group.species == "rat":
                    PHENOGEN_COORDS = "%d, %d, %d, %d" % (
                        xBrowse1, phenogenPaddingTop, xBrowse2, (phenogenPaddingTop + self.BAND_HEIGHT))
                    if self.dataset.group.species == "mouse":
                        PHENOGEN_HREF = "https://phenogen.org/gene.jsp?speciesCB=Mm&auto=Y&geneTxt=chr%s:%d-%d&genomeVer=mm10" % (
                            self.selectedChr, max(0, calBase - flankingWidthInBases), calBase + flankingWidthInBases)
                    else:
                        PHENOGEN_HREF = "https://phenogen.org/gene.jsp?speciesCB=Mm&auto=Y&geneTxt=chr%s:%d-%d&genomeVer=mm10" % (
                            self.selectedChr, max(0, calBase - flankingWidthInBases), calBase + flankingWidthInBases)
                    PHENOGEN_TITLE = "Click to view this section of the genome in PhenoGen"
                    gifmap.append(
                        HtmlGenWrapper.create_area_tag(
                            shape='rect',
                            coords=PHENOGEN_COORDS,
                            href=PHENOGEN_HREF,
                            title=PHENOGEN_TITLE))
                    im_drawer.rectangle(
                        xy=((xBrowse1, phenogenPaddingTop),
                            (xBrowse2, (phenogenPaddingTop + self.BAND_HEIGHT))),
                        outline=self.CLICKABLE_PHENOGEN_REGION_COLOR,
                        fill=self.CLICKABLE_PHENOGEN_REGION_COLOR)
                    im_drawer.line(
                        xy=((xBrowse1, phenogenPaddingTop), (xBrowse1,
                                                             (phenogenPaddingTop + self.BAND_HEIGHT))),
                        fill=self.CLICKABLE_PHENOGEN_REGION_OUTLINE_COLOR)

                UCSC_COORDS = "%d, %d, %d, %d" % (
                    xBrowse1, ucscPaddingTop, xBrowse2, (ucscPaddingTop + self.BAND_HEIGHT))
                if self.dataset.group.species == "mouse":
                    UCSC_HREF = "http://genome.ucsc.edu/cgi-bin/hgTracks?db=%s&position=chr%s:%d-%d&hgt.customText=%s/snp/chr%s" % (
                        self._ucscDb, self.selectedChr, max(0, calBase - flankingWidthInBases), calBase + flankingWidthInBases, webqtlConfig.PORTADDR, self.selectedChr)
                else:
                    UCSC_HREF = "http://genome.ucsc.edu/cgi-bin/hgTracks?db=%s&position=chr%s:%d-%d" % (
                        self._ucscDb, self.selectedChr, max(0, calBase - flankingWidthInBases), calBase + flankingWidthInBases)
                UCSC_TITLE = "Click to view this section of the genome in the UCSC Genome Browser"
                gifmap.append(
                    HtmlGenWrapper.create_area_tag(
                        shape='rect',
                        coords=UCSC_COORDS,
                        href=UCSC_HREF,
                        title=UCSC_TITLE))
                im_drawer.rectangle(
                    xy=((xBrowse1, ucscPaddingTop),
                        (xBrowse2, (ucscPaddingTop + self.BAND_HEIGHT))),
                    outline=self.CLICKABLE_UCSC_REGION_COLOR,
                    fill=self.CLICKABLE_UCSC_REGION_COLOR)
                im_drawer.line(
                    xy=((xBrowse1, ucscPaddingTop),
                        (xBrowse1, (ucscPaddingTop + self.BAND_HEIGHT))),
                    fill=self.CLICKABLE_UCSC_REGION_OUTLINE_COLOR)

                ENSEMBL_COORDS = "%d, %d, %d, %d" % (
                    xBrowse1, ensemblPaddingTop, xBrowse2, (ensemblPaddingTop + self.BAND_HEIGHT))
                if self.dataset.group.species == "mouse":
                    ENSEMBL_HREF = "http://www.ensembl.org/Mus_musculus/contigview?highlight=&chr=%s&vc_start=%d&vc_end=%d&x=35&y=12" % (
                        self.selectedChr, max(0, calBase - flankingWidthInBases), calBase + flankingWidthInBases)
                else:
                    ENSEMBL_HREF = "http://www.ensembl.org/Rattus_norvegicus/contigview?chr=%s&start=%d&end=%d" % (
                        self.selectedChr, max(0, calBase - flankingWidthInBases), calBase + flankingWidthInBases)
                ENSEMBL_TITLE = "Click to view this section of the genome in the Ensembl Genome Browser"
                gifmap.append(HtmlGenWrapper.create_area_tag(
                    shape='rect',
                    coords=ENSEMBL_COORDS,
                    href=ENSEMBL_HREF,
                    title=ENSEMBL_TITLE))
                im_drawer.rectangle(
                    xy=((xBrowse1, ensemblPaddingTop),
                        (xBrowse2, (ensemblPaddingTop + self.BAND_HEIGHT))),
                    outline=self.CLICKABLE_ENSEMBL_REGION_COLOR,
                    fill=self.CLICKABLE_ENSEMBL_REGION_COLOR)
                im_drawer.line(
                    xy=((xBrowse1, ensemblPaddingTop),
                        (xBrowse1, (ensemblPaddingTop + self.BAND_HEIGHT))),
                    fill=self.CLICKABLE_ENSEMBL_REGION_OUTLINE_COLOR)
            # end for

            im_drawer.text(
                text="Click to view the corresponding section of the genome in an 8x expanded WebQTL map",
                xy=((xLeftOffset + 10), paddingTop),  # + self.BAND_HEIGHT/2),
                font=clickableRegionLabelFont,
                fill=self.CLICKABLE_WEBQTL_TEXT_COLOR)
            if self.dataset.group.species == "mouse" or self.dataset.group.species == "rat":
                im_drawer.text(
                    text="Click to view the corresponding section of the genome in PhenoGen",
                    # + self.BAND_HEIGHT/2),
                    xy=((xLeftOffset + 10), phenogenPaddingTop),
                    font=clickableRegionLabelFont, fill=self.CLICKABLE_PHENOGEN_TEXT_COLOR)
            im_drawer.text(
                text="Click to view the corresponding section of the genome in the UCSC Genome Browser",
                # + self.BAND_HEIGHT/2),
                xy=((xLeftOffset + 10), ucscPaddingTop),
                font=clickableRegionLabelFont, fill=self.CLICKABLE_UCSC_TEXT_COLOR)
            im_drawer.text(
                text="Click to view the corresponding section of the genome in the Ensembl Genome Browser",
                # + self.BAND_HEIGHT/2),
                xy=((xLeftOffset + 10), ensemblPaddingTop),
                font=clickableRegionLabelFont, fill=self.CLICKABLE_ENSEMBL_TEXT_COLOR)

            # draw the gray text
            chrFont = ImageFont.truetype(
                font=VERDANA_BOLD_FILE, size=26 * zoom)
            chrX = xLeftOffset + plotWidth - 2 - im_drawer.textsize(
                "Chr %s" % self.ChrList[self.selectedChr][0], font=chrFont)[0]
            im_drawer.text(
                text="Chr %s" % self.ChrList[self.selectedChr][0],
                xy=(chrX, paddingTop), font=chrFont, fill=GRAY)
            # end of drawBrowserClickableRegions
        else:
            # draw the gray text
            chrFont = ImageFont.truetype(font=VERDANA_FILE, size=26 * zoom)
            chrX = xLeftOffset + (plotWidth - im_drawer.textsize(
                "Chr %s" % currentChromosome, font=chrFont)[0]) / 2
            im_drawer.text(
                text="Chr %s" % currentChromosome, xy=(chrX, 32), font=chrFont,
                fill=GRAY)
            # end of drawBrowserClickableRegions
        pass

    def drawXAxis(self, canvas, drawAreaHeight, gifmap, plotXScale, showLocusForm, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
        im_drawer = ImageDraw.Draw(canvas)
        xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
        plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
        plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
        yZero = canvas.size[1] - yBottomOffset
        fontZoom = zoom
        if zoom == 2:
            fontZoom = 1.5

        # Parameters
        NUM_MINOR_TICKS = 5  # Number of minor ticks between major ticks
        X_MAJOR_TICK_THICKNESS = 3
        X_MINOR_TICK_THICKNESS = 1
        X_AXIS_THICKNESS = 1 * zoom

        # ======= Alex: Draw the X-axis labels (megabase location)
        MBLabelFont = ImageFont.truetype(font=VERDANA_FILE, size=15 * zoom)
        xMajorTickHeight = 10 * zoom  # How high the tick extends below the axis
        xMinorTickHeight = 5 * zoom
        xAxisTickMarkColor = BLACK
        xAxisLabelColor = BLACK
        fontHeight = 12 * fontZoom  # How tall the font that we're using is
        spacingFromLabelToAxis = 10

        if self.plotScale == 'physic':
            strYLoc = yZero + MBLabelFont.font.height / 2
            # Physical single chromosome view
            if self.selectedChr > -1:
                XScale = Plot.detScale(startMb, endMb)
                XStart, XEnd, XStep = XScale
                if XStep < 8:
                    XStep *= 2
                spacingAmtX = spacingAmt = (XEnd - XStart) / XStep

                j = 0
                while abs(spacingAmtX - int(spacingAmtX)) >= spacingAmtX / 100.0 and j < 6:
                    j += 1
                    spacingAmtX *= 10

                formatStr = '%%2.%df' % j

                for counter, _Mb in enumerate(Plot.frange(XStart, XEnd, spacingAmt / NUM_MINOR_TICKS)):
                    if _Mb < startMb or _Mb > endMb:
                        continue
                    Xc = xLeftOffset + plotXScale * (_Mb - startMb)
                    if counter % NUM_MINOR_TICKS == 0:  # Draw a MAJOR mark, not just a minor tick mark
                        im_drawer.line(xy=((Xc, yZero),
                                           (Xc, yZero + xMajorTickHeight)),
                                       fill=xAxisTickMarkColor,
                                       width=X_MAJOR_TICK_THICKNESS)  # Draw the MAJOR tick mark
                        # What Mbase location to put on the label
                        labelStr = str(formatStr % _Mb)
                        strWidth, strHeight = im_drawer.textsize(
                            labelStr, font=MBLabelFont)
                        drawStringXc = (Xc - (strWidth / 2.0))
                        im_drawer.text(xy=(drawStringXc, strYLoc),
                                       text=labelStr, font=MBLabelFont,
                                       fill=xAxisLabelColor)
                    else:
                        im_drawer.line(xy=((Xc, yZero),
                                           (Xc, yZero + xMinorTickHeight)),
                                       fill=xAxisTickMarkColor,
                                       width=X_MINOR_TICK_THICKNESS)  # Draw the MINOR tick mark

            # Physical genome wide view
            else:
                distScale = 0
                startPosX = xLeftOffset
                for i, distLen in enumerate(self.ChrLengthDistList):
                    if distScale == 0:  # universal scale in whole genome mapping
                        if distLen > 75:
                            distScale = 25
                        elif distLen > 30:
                            distScale = 10
                        else:
                            distScale = 5
                    for j, tickdists in enumerate(range(distScale, int(ceil(distLen)), distScale)):
                        im_drawer.line(
                            xy=((startPosX + tickdists * plotXScale, yZero),
                                (startPosX + tickdists * plotXScale, yZero + 7)),
                            fill=BLACK, width=1 * zoom)
                        if j % 2 == 0:
                            draw_rotated_text(
                                canvas, text=str(tickdists), font=MBLabelFont,
                                xy=(startPosX + tickdists * plotXScale,
                                    yZero + 10 * zoom), fill=BLACK, angle=270)
                    startPosX += (self.ChrLengthDistList[i] + \
                                  self.GraphInterval) * plotXScale

            megabaseLabelFont = ImageFont.truetype(
                font=VERDANA_FILE, size=int(18 * zoom * 1.5))
            im_drawer.text(
                text="Megabases",
                xy=(
                    xLeftOffset + (plotWidth - im_drawer.textsize(
                        "Megabases", font=megabaseLabelFont)[0]) / 2,
                    strYLoc + MBLabelFont.font.height + 10 * (zoom % 2)),
                font=megabaseLabelFont, fill=BLACK)
            pass
        else:
            strYLoc = yZero + spacingFromLabelToAxis + MBLabelFont.font.height / 2
            ChrAInfo = []
            preLpos = -1
            distinctCount = 0.0

            if self.selectedChr == -1:  # ZS: If viewing full genome/all chromosomes
                for i, _chr in enumerate(self.genotype):
                    thisChr = []
                    Locus0CM = _chr[0].cM
                    nLoci = len(_chr)
                    if nLoci <= 8:
                        for _locus in _chr:
                            if _locus.name != ' - ':
                                if _locus.cM != preLpos:
                                    distinctCount += 1
                                preLpos = _locus.cM
                                thisChr.append(
                                    [_locus.name, _locus.cM - Locus0CM])
                    else:
                        for j in (0, round(nLoci / 4), round(nLoci / 2), round(nLoci * 3 / 4), -1):
                            while _chr[j].name == ' - ':
                                j += 1
                            if _chr[j].cM != preLpos:
                                distinctCount += 1
                            preLpos = _chr[j].cM
                            thisChr.append(
                                [_chr[j].name, _chr[j].cM - Locus0CM])
                    ChrAInfo.append(thisChr)
            else:
                for i, _chr in enumerate(self.genotype):
                    if _chr.name == self.ChrList[self.selectedChr][0]:
                        thisChr = []
                        Locus0CM = _chr[0].cM
                        for _locus in _chr:
                            if _locus.name != ' - ':
                                if _locus.cM != preLpos:
                                    distinctCount += 1
                                preLpos = _locus.cM
                                thisChr.append(
                                    [_locus.name, _locus.cM - Locus0CM])
                        ChrAInfo.append(thisChr)

            stepA = (plotWidth + 0.0) / distinctCount

            LRectWidth = 10
            LRectHeight = 3
            offsetA = -stepA
            lineColor = LIGHTBLUE
            startPosX = xLeftOffset

            for j, ChrInfo in enumerate(ChrAInfo):
                preLpos = -1
                for i, item in enumerate(ChrInfo):
                    Lname, Lpos = item
                    if Lpos != preLpos:
                        offsetA += stepA
                        differ = 1
                    else:
                        differ = 0
                    preLpos = Lpos
                    Lpos *= plotXScale
                    if self.selectedChr > -1:
                        Zorder = i % 5
                    else:
                        Zorder = 0
                    if differ:
                        im_drawer.line(
                            xy=((startPosX + Lpos, yZero), (xLeftOffset + offsetA,\
                                                            yZero + 25)),
                            fill=lineColor)
                        im_drawer.line(
                            xy=((xLeftOffset + offsetA, yZero + 25), (xLeftOffset + offsetA,\
                                                                      yZero + 40 + Zorder * (LRectWidth + 3))),
                            fill=lineColor)
                        rectColor = ORANGE
                    else:
                        im_drawer.line(
                            xy=((xLeftOffset + offsetA, yZero + 40 + Zorder * (LRectWidth + 3) - 3), (\
                                xLeftOffset + offsetA, yZero + 40 + Zorder * (LRectWidth + 3))),
                            fill=lineColor)
                        rectColor = DEEPPINK
                    im_drawer.rectangle(
                        xy=((xLeftOffset + offsetA, yZero + 40 + Zorder * (LRectWidth + 3)),
                            (xLeftOffset + offsetA - LRectHeight,
                             yZero + 40 + Zorder * (LRectWidth + 3) + LRectWidth)),
                        outline=rectColor, fill=rectColor, width=0)
                    COORDS = "%d,%d,%d,%d" % (xLeftOffset + offsetA - LRectHeight, yZero + 40 + Zorder * (LRectWidth + 3),\
                                              xLeftOffset + offsetA, yZero + 40 + Zorder * (LRectWidth + 3) + LRectWidth)
                    HREF = "/show_trait?trait_id=%s&dataset=%s" % (
                        Lname, self.dataset.group.name + "Geno")
                    #HREF="javascript:showDatabase3('%s','%s','%s','');" % (showLocusForm,fd.RISet+"Geno", Lname)
                    Areas = HtmlGenWrapper.create_area_tag(
                        shape='rect',
                        coords=COORDS,
                        href=HREF,
                        target="_blank",
                        title="Locus : {}".format(Lname))
                    gifmap.append(Areas)
                # piddle bug
                if j == 0:
                    im_drawer.line(
                        xy=((startPosX, yZero), (startPosX, yZero + 40)),
                        fill=lineColor)
                startPosX += (self.ChrLengthDistList[j] + \
                              self.GraphInterval) * plotXScale

            centimorganLabelFont = ImageFont.truetype(
                font=VERDANA_FILE, size=int(18 * zoom * 1.5))
            im_drawer.text(
                text="Centimorgans",
                xy=(xLeftOffset + (plotWidth - im_drawer.textsize(
                    "Centimorgans", font=centimorganLabelFont)[0]) / 2,
                    strYLoc + MBLabelFont.font.height + 10 * (zoom % 2)),
                font=centimorganLabelFont, fill=BLACK)

        im_drawer.line(xy=((xLeftOffset, yZero), (xLeftOffset + plotWidth, yZero)),
                       fill=BLACK, width=X_AXIS_THICKNESS)  # Draw the X axis itself

    def drawQTL(self, canvas, drawAreaHeight, gifmap, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
        im_drawer = ImageDraw.Draw(canvas)
        xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
        plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
        plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
        fontZoom = zoom
        if zoom == 2:
            fontZoom = 1.5

        INTERCROSS = (self.genotype.type == "intercross")

        # draw the LRS scale
        # We first determine whether or not we are using a sliding scale.
        # If so, we need to compute the maximum LRS value to determine where the max y-value should be, and call this LRS_LOD_Max.
        # LRSTop is then defined to be above the LRS_LOD_Max by enough to add one additional LRSScale increment.
        # if we are using a set-scale, then we set LRSTop to be the user's value, and LRS_LOD_Max doesn't matter.

        # ZS: This is a mess, but I don't know a better way to account for different mapping methods returning results in different formats + the option to change between LRS and LOD
        if self.lrsMax <= 0:  # sliding scale
            if "lrs_value" in self.qtlresults[0]:
                LRS_LOD_Max = max([result['lrs_value']
                                   for result in self.qtlresults])
                if self.LRS_LOD == "LOD" or self.LRS_LOD == "-logP":
                    LRS_LOD_Max = LRS_LOD_Max / self.LODFACTOR
                    if self.permChecked and self.nperm > 0 and not self.multipleInterval:
                        self.significant = min(
                            self.significant / self.LODFACTOR, webqtlConfig.MAXLRS)
                        self.suggestive = min(
                            self.suggestive / self.LODFACTOR, webqtlConfig.MAXLRS)
                else:
                    if self.permChecked and self.nperm > 0 and not self.multipleInterval:
                        self.significant = min(
                            self.significant, webqtlConfig.MAXLRS)
                        self.suggestive = min(
                            self.suggestive, webqtlConfig.MAXLRS)
                    else:
                        pass
            else:
                LRS_LOD_Max = max([result['lod_score']
                                   for result in self.qtlresults]) + 1
                if self.LRS_LOD == "LRS":
                    LRS_LOD_Max = LRS_LOD_Max * self.LODFACTOR
                    if self.permChecked and self.nperm > 0 and not self.multipleInterval:
                        self.significant = min(
                            self.significant * self.LODFACTOR, webqtlConfig.MAXLRS)
                        self.suggestive = min(
                            self.suggestive * self.LODFACTOR, webqtlConfig.MAXLRS)
                else:
                    if self.permChecked and self.nperm > 0 and not self.multipleInterval:
                        self.significant = min(
                            self.significant, webqtlConfig.MAXLRS)
                        self.suggestive = min(
                            self.suggestive, webqtlConfig.MAXLRS)
                    else:
                        pass

            if self.permChecked and self.nperm > 0 and not self.multipleInterval:
                LRS_LOD_Max = max(self.significant, LRS_LOD_Max)

            # genotype trait will give infinite LRS
            LRS_LOD_Max = min(LRS_LOD_Max, webqtlConfig.MAXLRS)
        else:
            LRS_LOD_Max = self.lrsMax

        # ZS: Needed to pass to genome browser
        js_data = json.loads(self.js_data)
        if self.LRS_LOD == "LRS":
            js_data['max_score'] = LRS_LOD_Max / 4.61
        else:
            js_data['max_score'] = LRS_LOD_Max
        self.js_data = json.dumps(js_data)

        LRSScaleFont = ImageFont.truetype(font=VERDANA_FILE, size=16 * zoom)
        LRSLODFont = ImageFont.truetype(
            font=VERDANA_FILE, size=int(18 * zoom * 1.5))

        yZero = yTopOffset + plotHeight
        LRSHeightThresh = drawAreaHeight
        AdditiveHeightThresh = drawAreaHeight / 2
        DominanceHeightThresh = drawAreaHeight / 2

        if LRS_LOD_Max > 100:
            LRSScale = 20.0
        elif LRS_LOD_Max > 20:
            LRSScale = 5.0
        elif LRS_LOD_Max > 7.5:
            LRSScale = 2.5
        else:
            LRSScale = 1.0

        LRSAxisList = Plot.frange(LRSScale, LRS_LOD_Max, LRSScale)

        # ZS: Convert to int if all axis values are whole numbers
        all_int = True
        for item in LRSAxisList:
            if isinstance(item, int):
                continue
            else:
                all_int = False
                break
        # TODO(PIL): Convert from PIL
        # if all_int:
        #     max_lrs_width = canvas.stringWidth("%d" % LRS_LOD_Max, font=LRSScaleFont) + 40
        # else:
        #     max_lrs_width = canvas.stringWidth("%2.1f" % LRS_LOD_Max, font=LRSScaleFont) + 30

        # draw the "LRS" or "LOD" string to the left of the axis
        LRSScaleFont = ImageFont.truetype(font=VERDANA_FILE, size=16 * zoom)
        LRSLODFont = ImageFont.truetype(
            font=VERDANA_FILE, size=int(18 * zoom * 1.5))
        yZero = yTopOffset + plotHeight

        # TEXT_X_DISPLACEMENT = -20
        #TEXT_Y_DISPLACEMENT = -215
        if all_int:
            TEXT_X_DISPLACEMENT = -12
        else:
            TEXT_X_DISPLACEMENT = -30
        if self.LRS_LOD == "-logP":
            TEXT_Y_DISPLACEMENT = -242
        else:
            TEXT_Y_DISPLACEMENT = -210
        draw_rotated_text(
            canvas, text=self.LRS_LOD, font=LRSLODFont,
            xy=(xLeftOffset - im_drawer.textsize(
                "999.99", font=LRSScaleFont)[0] - 15 * (zoom - 1) + TEXT_X_DISPLACEMENT,
                yZero + TEXT_Y_DISPLACEMENT - 300 * (zoom - 1)),
            fill=BLACK, angle=90)

        for item in LRSAxisList:
            if LRS_LOD_Max == 0.0:
                LRS_LOD_Max = 0.000001
            yTopOffset + 30 * (zoom - 1)
            yLRS = yZero - (item / LRS_LOD_Max) * LRSHeightThresh
            im_drawer.line(xy=((xLeftOffset, yLRS), (xLeftOffset - 4, yLRS)),
                           fill=self.LRS_COLOR, width=1 * zoom)
            if all_int:
                scaleStr = "%d" % item
            else:
                scaleStr = "%2.1f" % item
            # Draw the LRS/LOD Y axis label
            TEXT_Y_DISPLACEMENT = -10
            im_drawer.text(
                text=scaleStr,
                xy=(xLeftOffset - 4 - im_drawer.textsize(scaleStr, font=LRSScaleFont)[0] - 5,
                    yLRS + TEXT_Y_DISPLACEMENT),
                font=LRSScaleFont, fill=self.LRS_COLOR)

        if self.permChecked and self.nperm > 0 and not self.multipleInterval:
            significantY = yZero - self.significant * LRSHeightThresh / LRS_LOD_Max
            suggestiveY = yZero - self.suggestive * LRSHeightThresh / LRS_LOD_Max
            # significantY = yZero - self.significant*LRSHeightThresh/LRSAxisList[-1]
            # suggestiveY = yZero - self.suggestive*LRSHeightThresh/LRSAxisList[-1]
            startPosX = xLeftOffset

            # "Significant" and "Suggestive" Drawing Routine
            # ======= Draw the thick lines for "Significant" and "Suggestive" =====  (crowell: I tried to make the SNPs draw over these lines, but piddle wouldn't have it...)

            # ZS: I don't know if what I did here with this inner function is clever or overly complicated, but it's the only way I could think of to avoid duplicating the code inside this function
            def add_suggestive_significant_lines_and_legend(start_pos_x, chr_length_dist):
                rightEdge = xLeftOffset + plotWidth
                im_drawer.line(
                    xy=((start_pos_x + self.SUGGESTIVE_WIDTH / 1.5, suggestiveY),
                        (rightEdge, suggestiveY)),
                    fill=self.SUGGESTIVE_COLOR, width=self.SUGGESTIVE_WIDTH * zoom
                    # ,clipX=(xLeftOffset, xLeftOffset + plotWidth-2)
                )
                im_drawer.line(
                    xy=((start_pos_x + self.SUGGESTIVE_WIDTH / 1.5, significantY),
                        (rightEdge, significantY)),
                    fill=self.SIGNIFICANT_COLOR,
                    width=self.SIGNIFICANT_WIDTH * zoom
                    # , clipX=(xLeftOffset, xLeftOffset + plotWidth-2)
                )
                sugg_coords = "%d, %d, %d, %d" % (
                    start_pos_x, suggestiveY - 2, rightEdge + 2 * zoom, suggestiveY + 2)
                sig_coords = "%d, %d, %d, %d" % (
                    start_pos_x, significantY - 2, rightEdge + 2 * zoom, significantY + 2)

                if self.LRS_LOD == 'LRS':
                    sugg_title = "Suggestive LRS = %0.2f" % self.suggestive
                    sig_title = "Significant LRS = %0.2f" % self.significant
                else:
                    sugg_title = "Suggestive LOD = %0.2f" % (
                        self.suggestive / 4.61)
                    sig_title = "Significant LOD = %0.2f" % (
                        self.significant / 4.61)
                Areas1 = HtmlGenWrapper.create_area_tag(
                    shape='rect',
                    coords=sugg_coords,
                    title=sugg_title)
                Areas2 = HtmlGenWrapper.create_area_tag(
                    shape='rect',
                    coords=sig_coords,
                    title=sig_title)
                gifmap.append(Areas1)
                gifmap.append(Areas2)

                start_pos_x += (chr_length_dist + \
                                self.GraphInterval) * plotXScale
                return start_pos_x

            for i, _chr in enumerate(self.genotype):
                if self.selectedChr != -1:
                    if _chr.name == self.ChrList[self.selectedChr][0]:
                        startPosX = add_suggestive_significant_lines_and_legend(
                            startPosX, self.ChrLengthDistList[0])
                        break
                    else:
                        continue
                else:
                    startPosX = add_suggestive_significant_lines_and_legend(
                        startPosX, self.ChrLengthDistList[i])

        if self.multipleInterval:
            lrsEdgeWidth = 1
        else:
            if self.additiveChecked:
                additiveMax = max([abs(X['additive'])
                                   for X in self.qtlresults])
            lrsEdgeWidth = 3

        if zoom == 2:
            lrsEdgeWidth = 2 * lrsEdgeWidth

        LRSCoordXY = []
        AdditiveCoordXY = []
        DominanceCoordXY = []

        symbolFont = ImageFont.truetype(
            font=FNT_BS_FILE, size=5)  # ZS: For Manhattan Plot

        previous_chr = 1
        previous_chr_as_int = 0
        lineWidth = 1
        oldStartPosX = 0
        startPosX = xLeftOffset
        for i, qtlresult in enumerate(self.qtlresults):
            m = 0
            thisLRSColor = self.colorCollection[0]
            if qtlresult['chr'] != previous_chr and self.selectedChr == -1:
                if self.manhattan_plot != True and len(LRSCoordXY) > 1:
                    draw_open_polygon(canvas, xy=LRSCoordXY,
                                      outline=thisLRSColor, width=lrsEdgeWidth)

                if not self.multipleInterval and self.additiveChecked:
                    plusColor = self.ADDITIVE_COLOR_POSITIVE
                    minusColor = self.ADDITIVE_COLOR_NEGATIVE
                    for k, aPoint in enumerate(AdditiveCoordXY):
                        if k > 0:
                            Xc0, Yc0 = AdditiveCoordXY[k - 1]
                            Xc, Yc = aPoint
                            if (Yc0 - yZero) * (Yc - yZero) < 0:
                                if Xc == Xc0:  # genotype , locus distance is 0
                                    Xcm = Xc
                                else:
                                    Xcm = (yZero - Yc0) / \
                                        ((Yc - Yc0) / (Xc - Xc0)) + Xc0
                                if Yc0 < yZero:
                                    im_drawer.line(
                                        xy=((Xc0, Yc0), (Xcm, yZero)),
                                        fill=plusColor, width=lineWidth
                                    )
                                    im_drawer.line(
                                        xy=((Xcm, yZero),
                                            (Xc, yZero - (Yc - yZero))),
                                        fill=minusColor, width=lineWidth
                                    )
                                else:
                                    im_drawer.line(
                                        xy=((Xc0, yZero - (Yc0 - yZero)),
                                            (Xcm, yZero)),
                                        fill=minusColor, width=lineWidth
                                    )
                                    im_drawer.line(
                                        xy=((Xcm, yZero), (Xc, Yc)),
                                        fill=plusColor, width=lineWidth
                                    )
                            elif (Yc0 - yZero) * (Yc - yZero) > 0:
                                if Yc < yZero:
                                    im_drawer.line(
                                        xy=((Xc0, Yc0), (Xc, Yc)),
                                        fill=plusColor,
                                        width=lineWidth
                                    )
                                else:
                                    im_drawer.line(
                                        xy=((Xc0, yZero - (Yc0 - yZero)),
                                            (Xc, yZero - (Yc - yZero))),
                                        fill=minusColor, width=lineWidth
                                    )
                            else:
                                minYc = min(Yc - yZero, Yc0 - yZero)
                                if minYc < 0:
                                    im_drawer.line(
                                        xy=((Xc0, Yc0), (Xc, Yc)),
                                        fill=plusColor, width=lineWidth
                                    )
                                else:
                                    im_drawer.line(
                                        xy=((Xc0, yZero - (Yc0 - yZero)),
                                            (Xc, yZero - (Yc - yZero))),
                                        fill=minusColor, width=lineWidth
                                    )

                LRSCoordXY = []
                AdditiveCoordXY = []
                previous_chr = qtlresult['chr']
                previous_chr_as_int += 1
                newStartPosX = (
                    self.ChrLengthDistList[previous_chr_as_int - 1] + self.GraphInterval) * plotXScale
                if newStartPosX != oldStartPosX:
                    startPosX += newStartPosX
                    oldStartPosX = newStartPosX

            # This is because the chromosome value stored in qtlresult['chr'] can be (for example) either X or 20 depending upon the mapping method/scale used
            this_chr = str(self.ChrList[self.selectedChr][0])
            if self.plotScale != "physic":
                this_chr = str(self.ChrList[self.selectedChr][1] + 1)

            if self.selectedChr == -1 or str(qtlresult['chr']) == this_chr:
                if self.plotScale != "physic" and self.mapping_method == "reaper" and not self.manhattan_plot:
                    start_cm = self.genotype[self.selectedChr - 1][0].cM
                    Xc = startPosX + (qtlresult['cM'] - start_cm) * plotXScale
                    if hasattr(self.genotype, "filler"):
                        if self.genotype.filler:
                            if self.selectedChr != -1:
                                Xc = startPosX + \
                                    (qtlresult['Mb'] - start_cm) * plotXScale
                            else:
                                Xc = startPosX + ((qtlresult['Mb'] - start_cm - startMb) * plotXScale) * (
                                    ((qtlresult['Mb'] - start_cm - startMb) * plotXScale) / ((qtlresult['Mb'] - start_cm - startMb + self.GraphInterval) * plotXScale))
                else:
                    if self.selectedChr != -1 and qtlresult['Mb'] > endMb:
                        Xc = startPosX + endMb * plotXScale
                    else:
                        if qtlresult['Mb'] - startMb < 0:
                            continue
                        Xc = startPosX + (qtlresult['Mb'] - startMb) * plotXScale

                # updated by NL 06-18-2011:
                # fix the over limit LRS graph issue since genotype trait may give infinite LRS;
                # for any lrs is over than 460(LRS max in this system), it will be reset to 460

                yLRS = yZero - (item / LRS_LOD_Max) * LRSHeightThresh

                if 'lrs_value' in qtlresult:
                    if self.LRS_LOD == "LOD" or self.LRS_LOD == "-logP":
                        if qtlresult['lrs_value'] > 460 or qtlresult['lrs_value'] == 'inf':
                            Yc = yZero - webqtlConfig.MAXLRS * \
                                LRSHeightThresh / \
                                (LRS_LOD_Max * self.LODFACTOR)
                        else:
                            Yc = yZero - \
                                qtlresult['lrs_value'] * LRSHeightThresh / \
                                (LRS_LOD_Max * self.LODFACTOR)
                    else:
                        if qtlresult['lrs_value'] > 460 or qtlresult['lrs_value'] == 'inf':
                            Yc = yZero - webqtlConfig.MAXLRS * LRSHeightThresh / LRS_LOD_Max
                        else:
                            Yc = yZero - \
                                qtlresult['lrs_value'] * \
                                LRSHeightThresh / LRS_LOD_Max
                else:
                    if qtlresult['lod_score'] > 100 or qtlresult['lod_score'] == 'inf':
                        Yc = yZero - webqtlConfig.MAXLRS * LRSHeightThresh / LRS_LOD_Max
                    else:
                        if self.LRS_LOD == "LRS":
                            Yc = yZero - \
                                qtlresult['lod_score'] * self.LODFACTOR * \
                                LRSHeightThresh / LRS_LOD_Max
                        else:
                            Yc = yZero - \
                                qtlresult['lod_score'] * \
                                LRSHeightThresh / LRS_LOD_Max

                if self.manhattan_plot == True:
                    if self.color_scheme == "single":
                        point_color = self.manhattan_single_color
                    elif self.color_scheme == "varied":
                        point_color = DISTINCT_COLOR_LIST[previous_chr_as_int]
                    else:
                        if self.selectedChr == -1 and (previous_chr_as_int % 2 == 1):
                            point_color = RED
                        else:
                            point_color = BLUE

                    im_drawer.text(
                        text="5",
                        xy=(
                            Xc - im_drawer.textsize("5",
                                                    font=symbolFont)[0] / 2 + 1,
                            Yc - 4),
                        fill=point_color, font=symbolFont)
                else:
                    LRSCoordXY.append((Xc, Yc))

                if not self.multipleInterval and self.additiveChecked:
                    if additiveMax == 0.0:
                        additiveMax = 0.000001
                    Yc = yZero - qtlresult['additive'] * \
                        AdditiveHeightThresh / additiveMax
                    AdditiveCoordXY.append((Xc, Yc))

                if self.selectedChr != -1 and qtlresult['Mb'] > endMb and endMb != -1:
                    break
                m += 1

        if self.manhattan_plot != True and len(LRSCoordXY) > 1:
            draw_open_polygon(canvas, xy=LRSCoordXY, outline=thisLRSColor,
                              width=lrsEdgeWidth)

        if not self.multipleInterval and self.additiveChecked:
            plusColor = self.ADDITIVE_COLOR_POSITIVE
            minusColor = self.ADDITIVE_COLOR_NEGATIVE
            for k, aPoint in enumerate(AdditiveCoordXY):
                if k > 0:
                    Xc0, Yc0 = AdditiveCoordXY[k - 1]
                    Xc, Yc = aPoint
                    if (Yc0 - yZero) * (Yc - yZero) < 0:
                        if Xc == Xc0:  # genotype , locus distance is 0
                            Xcm = Xc
                        else:
                            Xcm = (yZero - Yc0) / \
                                ((Yc - Yc0) / (Xc - Xc0)) + Xc0
                        if Yc0 < yZero:
                            im_drawer.line(
                                xy=((Xc0, Yc0), (Xcm, yZero)),
                                fill=plusColor, width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )
                            im_drawer.line(
                                xy=((Xcm, yZero), (Xc, yZero - (Yc - yZero))),
                                fill=minusColor, width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )
                        else:
                            im_drawer.line(
                                xy=((Xc0, yZero - (Yc0 - yZero)),
                                    (Xcm, yZero)),
                                fill=minusColor, width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )
                            im_drawer.line(
                                xy=((Xcm, yZero), (Xc, Yc)),
                                fill=plusColor, width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )
                    elif (Yc0 - yZero) * (Yc - yZero) > 0:
                        if Yc < yZero:
                            im_drawer.line(
                                xy=((Xc0, Yc0), (Xc, Yc)), fill=plusColor,
                                width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )
                        else:
                            im_drawer.line(
                                xy=((Xc0, yZero - (Yc0 - yZero)),
                                    (Xc, yZero - (Yc - yZero))),
                                fill=minusColor, width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )
                    else:
                        minYc = min(Yc - yZero, Yc0 - yZero)
                        if minYc < 0:
                            im_drawer.line(
                                xy=((Xc0, Yc0), (Xc, Yc)),
                                fill=plusColor, width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )
                        else:
                            im_drawer.line(
                                xy=((Xc0, yZero - (Yc0 - yZero)),
                                    (Xc, yZero - (Yc - yZero))),
                                fill=minusColor, width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )

        if not self.multipleInterval and INTERCROSS and self.dominanceChecked:
            plusColor = self.DOMINANCE_COLOR_POSITIVE
            minusColor = self.DOMINANCE_COLOR_NEGATIVE
            for k, aPoint in enumerate(DominanceCoordXY):
                if k > 0:
                    Xc0, Yc0 = DominanceCoordXY[k - 1]
                    Xc, Yc = aPoint
                    if (Yc0 - yZero) * (Yc - yZero) < 0:
                        if Xc == Xc0:  # genotype , locus distance is 0
                            Xcm = Xc
                        else:
                            Xcm = (yZero - Yc0) / \
                                ((Yc - Yc0) / (Xc - Xc0)) + Xc0
                        if Yc0 < yZero:
                            im_drawer.line(
                                xy=((Xc0, Yc0), (Xcm, yZero)),
                                fill=plusColor, width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )
                            im_drawer.line(
                                xy=((Xcm, yZero), (Xc, yZero - (Yc - yZero))),
                                fill=minusColor, width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )
                        else:
                            im_drawer.line(
                                xy=((Xc0, yZero - (Yc0 - yZero)), (Xcm, yZero)),
                                fill=minusColor, width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )
                            im_drawer.line(
                                xy=((Xcm, yZero), (Xc, Yc)),
                                fill=plusColor, width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )
                    elif (Yc0 - yZero) * (Yc - yZero) > 0:
                        if Yc < yZero:
                            im_drawer.line(
                                xy=((Xc0, Yc0), (Xc, Yc)),
                                fill=plusColor, width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )
                        else:
                            im_drawer.line(
                                xy=((Xc0, yZero - (Yc0 - yZero)),
                                    (Xc, yZero - (Yc - yZero))),
                                fill=minusColor, width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )
                    else:
                        minYc = min(Yc - yZero, Yc0 - yZero)
                        if minYc < 0:
                            im_drawer.line(
                                xy=((Xc0, Yc0), (Xc, Yc)),
                                fill=plusColor, width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )
                        else:
                            im_drawer.line(
                                xy=((Xc0, yZero - (Yc0 - yZero)),
                                    (Xc, yZero - (Yc - yZero))), fill=minusColor,
                                width=lineWidth
                                # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
                            )

        # draw additive scale
        if not self.multipleInterval and self.additiveChecked:
            additiveScaleFont = ImageFont.truetype(
                font=VERDANA_FILE, size=16 * zoom)
            additiveScale = Plot.detScaleOld(0, additiveMax)
            additiveStep = (additiveScale[1] - \
                            additiveScale[0]) / additiveScale[2]
            additiveAxisList = Plot.frange(0, additiveScale[1], additiveStep)
            addPlotScale = AdditiveHeightThresh / additiveMax
            TEXT_Y_DISPLACEMENT = -8

            additiveAxisList.append(additiveScale[1])
            for item in additiveAxisList:
                additiveY = yZero - item * addPlotScale
                im_drawer.line(
                    xy=((xLeftOffset + plotWidth, additiveY),
                        (xLeftOffset + 4 + plotWidth, additiveY)),
                    fill=self.ADDITIVE_COLOR_POSITIVE, width=1 * zoom)
                scaleStr = "%2.3f" % item
                im_drawer.text(
                    text=scaleStr,
                    xy=(xLeftOffset + plotWidth + 6,
                        additiveY + TEXT_Y_DISPLACEMENT),
                    font=additiveScaleFont, fill=self.ADDITIVE_COLOR_POSITIVE)

            im_drawer.line(
                xy=((xLeftOffset + plotWidth, additiveY),
                    (xLeftOffset + plotWidth, yZero)),
                fill=self.ADDITIVE_COLOR_POSITIVE, width=1 * zoom)

        im_drawer.line(
            xy=((xLeftOffset, yZero), (xLeftOffset, yTopOffset + 30 * (zoom - 1))),
            fill=self.LRS_COLOR, width=1 * zoom)  # the blue line running up the y axis

    def drawGraphBackground(self, canvas, gifmap, offset=(80, 120, 80, 50), zoom=1, startMb=None, endMb=None):
        # conditions
        # multiple Chromosome view
        # single Chromosome Physical
        # single Chromosome Genetic
        im_drawer = ImageDraw.Draw(canvas)
        xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
        plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
        plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
        yBottom = yTopOffset + plotHeight
        fontZoom = zoom
        if zoom == 2:
            fontZoom = 1.5
            yTopOffset += 30

        # calculate plot scale
        if self.plotScale != 'physic':
            self.ChrLengthDistList = self.ChrLengthCMList
            drawRegionDistance = self.ChrLengthCMSum
        else:
            self.ChrLengthDistList = self.ChrLengthMbList
            drawRegionDistance = self.ChrLengthMbSum

        if self.selectedChr > -1:  # single chromosome view
            spacingAmt = plotWidth / 13.5
            i = 0
            for startPix in Plot.frange(xLeftOffset, xLeftOffset + plotWidth, spacingAmt):
                if (i % 2 == 0):
                    theBackColor = self.GRAPH_BACK_DARK_COLOR
                else:
                    theBackColor = self.GRAPH_BACK_LIGHT_COLOR
                i += 1
                im_drawer.rectangle(
                    [(startPix, yTopOffset),
                     (min(startPix + spacingAmt, xLeftOffset + plotWidth), yBottom)],
                    outline=theBackColor, fill=theBackColor)

            drawRegionDistance = self.ChrLengthDistList[self.ChrList[self.selectedChr][1]]
            self.ChrLengthDistList = [drawRegionDistance]
            if self.plotScale == 'physic':
                plotXScale = plotWidth / (endMb - startMb)
            else:
                plotXScale = plotWidth / drawRegionDistance

        else:  # multiple chromosome view
            plotXScale = plotWidth / \
                ((len(self.genotype) - 1) * self.GraphInterval + drawRegionDistance)

            startPosX = xLeftOffset
            if fontZoom == 1.5:
                chrFontZoom = 2
            else:
                chrFontZoom = 1
            chrLabelFont = ImageFont.truetype(
                font=VERDANA_FILE, size=24 * chrFontZoom)

            for i, _chr in enumerate(self.genotype):
                if (i % 2 == 0):
                    theBackColor = self.GRAPH_BACK_DARK_COLOR
                else:
                    theBackColor = self.GRAPH_BACK_LIGHT_COLOR

                # draw the shaded boxes and the sig/sug thick lines
                im_drawer.rectangle(
                    ((startPosX, yTopOffset),
                     (startPosX + self.ChrLengthDistList[i] * plotXScale, yBottom)),
                    outline=GAINSBORO,
                    fill=theBackColor)

                chrNameWidth, chrNameHeight = im_drawer.textsize(
                    _chr.name, font=chrLabelFont)
                chrStartPix = startPosX + \
                    (self.ChrLengthDistList[i] * plotXScale - chrNameWidth) / 2
                chrEndPix = startPosX + \
                    (self.ChrLengthDistList[i] * plotXScale + chrNameWidth) / 2

                TEXT_Y_DISPLACEMENT = 0
                im_drawer.text(xy=(chrStartPix, yTopOffset + TEXT_Y_DISPLACEMENT),
                               text=_chr.name, font=chrLabelFont, fill=BLACK)
                COORDS = "%d,%d,%d,%d" % (
                    chrStartPix, yTopOffset, chrEndPix, yTopOffset + 20)

                # add by NL 09-03-2010
                HREF = "javascript:chrView(%d,%s);" % (i, self.ChrLengthMbList)
                Areas = HtmlGenWrapper.create_area_tag(
                    shape='rect',
                    coords=COORDS,
                    href=HREF)
                gifmap.append(Areas)
                startPosX += (self.ChrLengthDistList[i] + \
                              self.GraphInterval) * plotXScale

        return plotXScale

    def drawPermutationHistogram(self):
        #########################################
        #      Permutation Graph
        #########################################
        myCanvas = Image.new("RGBA", size=(500, 300))
        if 'lod_score' in self.qtlresults[0] and self.LRS_LOD == "LRS":
            perm_output = [value * 4.61 for value in self.perm_output]
        elif 'lod_score' not in self.qtlresults[0] and self.LRS_LOD == "LOD":
            perm_output = [value / 4.61 for value in self.perm_output]
        else:
            perm_output = self.perm_output

        filename = webqtlUtil.genRandStr("Reg_")
        Plot.plotBar(myCanvas, perm_output, XLabel=self.LRS_LOD,
                     YLabel='Frequency', title=' Histogram of Permutation Test')
        myCanvas.save("{}.gif".format(GENERATED_IMAGE_DIR + filename),
                      format='gif')

        return filename

    def geneTable(self, geneCol, refGene=None):
        if self.dataset.group.species == 'mouse' or self.dataset.group.species == 'rat':
            self.gene_table_header = self.getGeneTableHeaderList(refGene=None)
            self.gene_table_body = self.getGeneTableBody(geneCol, refGene=None)
        else:
            self.gene_table_header = None
            self.gene_table_body = None

    def getGeneTableHeaderList(self, refGene=None):

        gene_table_header_list = []
        if self.dataset.group.species == "mouse":
            if refGene:
                gene_table_header_list = ["Index",
                                          "Symbol",
                                          "Mb Start",
                                          "Length (Kb)",
                                          "SNP Count",
                                          "SNP Density",
                                          "Avg Expr",
                                          "Human Chr",
                                          "Mb Start (hg19)",
                                          "Literature Correlation",
                                          "Gene Description"]
            else:
                gene_table_header_list = ["",
                                          "Index",
                                          "Symbol",
                                          "Mb Start",
                                          "Length (Kb)",
                                          "SNP Count",
                                          "SNP Density",
                                          "Avg Expr",
                                          "Human Chr",
                                          "Mb Start (hg19)",
                                          "Gene Description"]
        elif self.dataset.group.species == "rat":
            gene_table_header_list = ["",
                                      "Index",
                                      "Symbol",
                                      "Mb Start",
                                      "Length (Kb)",
                                      "Avg Expr",
                                      "Mouse Chr",
                                      "Mb Start (mm9)",
                                      "Human Chr",
                                      "Mb Start (hg19)",
                                      "Gene Description"]

        return gene_table_header_list

    def getGeneTableBody(self, geneCol, refGene=None):
        gene_table_body = []

        tableIterationsCnt = 0
        if self.dataset.group.species == "mouse":
            for gIndex, theGO in enumerate(geneCol):

                tableIterationsCnt = tableIterationsCnt + 1

                this_row = []  # container for the cells of each row
                selectCheck = HtmlGenWrapper.create_input_tag(
                    type_="checkbox",
                    name="selectCheck",
                    value=theGO["GeneSymbol"],
                    Class="checkbox trait_checkbox")  # checkbox for each row

                geneLength = (theGO["TxEnd"] - theGO["TxStart"]) * 1000.0
                tenPercentLength = geneLength * 0.0001
                txStart = theGO["TxStart"]
                txEnd = theGO["TxEnd"]
                theGO["snpDensity"] = theGO["snpCount"] / geneLength
                if self.ALEX_DEBUG_BOOL_PRINT_GENE_LIST:
                    geneIdString = 'http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=gene&cmd=Retrieve&dopt=Graphics&list_uids=%s' % theGO[
                        "GeneID"]

                    if theGO["snpCount"]:
                        snpString = HT.Link(
                            (f"http://genenetwork.org/webqtl/main.py?FormID=snpBrowser&"
                             f"chr={theGO['Chr']}&"
                             f"start={theGO['TxStart']}&"
                             f"end={theGO['TxEnd']}&"
                             f"geneName={theGO['GeneSymbol']}&"
                             f"s1={self.diffCol[0]}&s2=%d"),
                            str(theGO["snpCount"])  # The text to display
                        )
                        snpString.set_blank_target()
                        snpString.set_attribute("class", "normalsize")
                    else:
                        snpString = 0

                    mouseStartString = "http://genome.ucsc.edu/cgi-bin/hgTracks?clade=vertebrate&org=Mouse&db=mm10&position=chr" + \
                        theGO["Chr"] + "%3A" + str(int(theGO["TxStart"] * 1000000.0)) + "-" + str(
                            int(theGO["TxEnd"] * 1000000.0)) + "&pix=620&Submit=submit"

                    # the chromosomes for human 1 are 1qXX.XX
                    if 'humanGene' in theGO:
                        if theGO['humanGene']["TxStart"] == '':
                            humanStartDisplay = ""
                        else:
                            humanStartDisplay = "%0.6f" % theGO['humanGene']["TxStart"]

                        humanChr = theGO['humanGene']["Chr"]
                        humanTxStart = theGO['humanGene']["TxStart"]

                        humanStartString = "http://genome.ucsc.edu/cgi-bin/hgTracks?clade=vertebrate&org=Human&db=hg17&position=chr%s:%d-%d" % (
                            humanChr, int(1000000 * theGO['humanGene']["TxStart"]), int(1000000 * theGO['humanGene']["TxEnd"]))
                    else:
                        humanStartString = humanChr = humanStartDisplay = "--"

                    geneDescription = theGO["GeneDescription"]
                    if len(geneDescription) > 70:
                        geneDescription = geneDescription[:70] + "..."

                    if theGO["snpDensity"] < 0.000001:
                        snpDensityStr = "0"
                    else:
                        snpDensityStr = "%0.6f" % theGO["snpDensity"]

                    avgExpr = []  # theGO["avgExprVal"]
                    if avgExpr in ([], None):
                        avgExpr = "--"
                    else:
                        avgExpr = "%0.6f" % avgExpr

                    # If we have a referenceGene then we will show the Literature Correlation
                    if theGO["Chr"] == "X":
                        chr_as_int = 19
                    else:
                        chr_as_int = int(theGO["Chr"]) - 1
                    if refGene:
                        literatureCorrelationString = str(self.getLiteratureCorrelation(
                            self.cursor, refGene, theGO['GeneID']) or "N/A")

                        this_row = [selectCheck.__str__(),
                                    str(tableIterationsCnt),
                                    str(HtmlGenWrapper.create_link_tag(
                                        geneIdString,
                                        theGO["GeneSymbol"],
                                        target="_blank")
                                        ),
                                    str(HtmlGenWrapper.create_link_tag(
                                        mouseStartString,
                                        "{:.6f}".format(txStart),
                                        target="_blank")
                                        ),
                                    str(HtmlGenWrapper.create_link_tag(
                                        "javascript:rangeView('{}', {:f}, {:f})".format(
                                            str(chr_as_int),
                                            txStart - tenPercentLength,
                                            txEnd + tenPercentLength),
                                        "{:.3f}".format(geneLength))),
                                    snpString,
                                    snpDensityStr,
                                    avgExpr,
                                    humanChr,
                                    str(HtmlGenWrapper.create_link_tag(
                                        humanStartString,
                                        humanStartDisplay,
                                        target="_blank")),
                                    literatureCorrelationString,
                                    geneDescription]
                    else:
                        this_row = [selectCheck.__str__(),
                                    str(tableIterationsCnt),
                                    str(HtmlGenWrapper.create_link_tag(
                                        geneIdString, theGO["GeneSymbol"],
                                        target="_blank")),
                                    str(HtmlGenWrapper.create_link_tag(
                                        mouseStartString,
                                        "{:.6f}".format(txStart),
                                        target="_blank")),
                                    str(HtmlGenWrapper.create_link_tag(
                                        "javascript:rangeView('{}', {:f}, {:f})".format(
                                            str(chr_as_int),
                                            txStart - tenPercentLength,
                                            txEnd + tenPercentLength),
                                        "{:.3f}".format(geneLength))),
                                    snpString,
                                    snpDensityStr,
                                    avgExpr,
                                    humanChr,
                                    str(HtmlGenWrapper.create_link_tag(
                                        humanStartString,
                                        humanStartDisplay,
                                        target="_blank")),
                                    geneDescription]

                gene_table_body.append(this_row)

        elif self.dataset.group.species == 'rat':
            for gIndex, theGO in enumerate(geneCol):
                this_row = []  # container for the cells of each row
                selectCheck = str(HtmlGenWrapper.create_input_tag(
                    type_="checkbox",
                    name="selectCheck",
                    Class="checkbox trait_checkbox"))  # checkbox for each row

                if theGO["GeneID"] != "":
                    geneSymbolNCBI = str(HtmlGenWrapper.create_link_tag(
                        "http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=gene&cmd=Retrieve&dopt=Graphics&list_uids={}".format(
                            theGO["GeneID"]),
                        theGO["GeneSymbol"],
                        Class="normalsize",
                        target="_blank"))
                else:
                    geneSymbolNCBI = theGO["GeneSymbol"]

                if theGO["Chr"] == "X":
                    chr_as_int = 20
                else:
                    chr_as_int = int(theGO["Chr"]) - 1

                geneLength = (float(theGO["TxEnd"]) - float(theGO["TxStart"]))
                geneLengthURL = "javascript:rangeView('%s', %f, %f)" % (theGO["Chr"], float(
                    theGO["TxStart"]) - (geneLength * 0.1), float(theGO["TxEnd"]) + (geneLength * 0.1))

                avgExprVal = []
                if avgExprVal != "" and avgExprVal:
                    avgExprVal = "%0.5f" % float(avgExprVal)
                else:
                    avgExprVal = ""

                # Mouse Gene
                if theGO['mouseGene']:
                    mouseChr = theGO['mouseGene']["Chr"]
                    mouseTxStart = "%0.6f" % theGO['mouseGene']["TxStart"]
                else:
                    mouseChr = mouseTxStart = ""

                # the chromosomes for human 1 are 1qXX.XX
                if 'humanGene' in theGO:
                    humanChr = theGO['humanGene']["Chr"]
                    humanTxStart = "%0.6f" % theGO['humanGene']["TxStart"]
                else:
                    humanChr = humanTxStart = ""

                geneDesc = theGO["GeneDescription"]
                if geneDesc == "---":
                    geneDesc = ""

                this_row = [selectCheck.__str__(),
                            str(gIndex + 1),
                            geneSymbolNCBI,
                            "%0.6f" % theGO["TxStart"],
                            str(HtmlGenWrapper.create_link_tag(
                                geneLengthURL,
                                "{:.3f}".format(geneLength * 1000.0))),
                            avgExprVal,
                            mouseChr,
                            mouseTxStart,
                            humanChr,
                            humanTxStart,
                            geneDesc]

                gene_table_body.append(this_row)

        return gene_table_body

    def getLiteratureCorrelation(cursor, geneId1=None, geneId2=None):
        if not geneId1 or not geneId2:
            return None
        if geneId1 == geneId2:
            return 1.0
        geneId1 = str(geneId1)
        geneId2 = str(geneId2)
        lCorr = None
        try:
            query = 'SELECT Value FROM LCorrRamin3 WHERE GeneId1 = %s and GeneId2 = %s'
            for x, y in [(geneId1, geneId2), (geneId2, geneId1)]:
                cursor.execute(query, (x, y))
                lCorr = cursor.fetchone()
                if lCorr:
                    lCorr = lCorr[0]
                    break
        except:
            raise  # lCorr = None
        return lCorr