0f09add63f6f06980b54fe6b18881d38747db60b
kate
  Thu Nov 19 12:20:50 2015 -0800
Add difference graph feature to GTEx track. refs #15645

diff --git src/hg/hgTracks/gtexTracks.c src/hg/hgTracks/gtexTracks.c
index 03439e5..f6fbbbc 100644
--- src/hg/hgTracks/gtexTracks.c
+++ src/hg/hgTracks/gtexTracks.c
@@ -11,32 +11,33 @@
 #include "gtexGeneBed.h"
 #include "gtexTissue.h"
 #include "gtexTissueData.h"
 #include "gtexUi.h"
 // TODO: move spaceSaver code to simpleTracks
 #include "spaceSaver.h"
 
 // NOTE: Sections to change for multi-region (vertical slice) display 
 //       are marked with #ifdef MULTI_REGION.  WARNING: These sections
 //       are a bit out-of-date (refer to #ifndef MULTI code when integrating)
 
 struct gtexGeneExtras 
 /* Track info */
     {
     double maxMedian;           /* Maximum median rpkm for all tissues */
-    boolean isComparison;       /* Comparison of two sample sets (e.g. male/female).
-                                       Displayed as two graphs, one oriented downward */
+    boolean isComparison;       /* Comparison of two sample sets (e.g. male/female). */
+    boolean isDifference;       /* True if comparison is shown as a single difference graph. 
+                                   False if displayed as two graphs, one oriented downward */
     char *graphType;            /* Additional info about graph (e.g. type of comparison graph */
     struct rgbColor *colors;    /* Color palette for tissues */
     };
 
 struct gtexGeneInfo
 /* GTEx gene model, names, and expression medians */
     {
     struct gtexGeneInfo *next;  /* Next in singly linked list */
     struct gtexGeneBed *geneBed;/* Gene name, id, canonical transcript, exp count and medians 
                                         from BED table */
     struct genePred *geneModel; /* Gene structure from model table */
     double *medians1;            /* Computed medians */
     double *medians2;            /* Computed medians for comparison (inverse) graph */
     int height;                  /* Item height in pixels */
     };
@@ -208,53 +209,73 @@
     double *medians2;
     AllocArray(medians1, expCount);
     AllocArray(medians2, expCount);
     int i;
     for (i=0; i<geneBed->expCount; i++)
         {
         //medians1[i] = -1, medians2[i] = -1;       // mark missing tissues ?
         struct slDouble *scores;
         scores = hashFindVal(scoreHash1, getTissueName(i));
         if (scores)
             medians1[i] = slDoubleMedian(scores);
         scores = hashFindVal(scoreHash2, getTissueName(i));
         if (scores)
             medians2[i] = slDoubleMedian(scores);
         }
+    if (extras->isDifference)
+        {
+        for (i=0; i<geneBed->expCount; i++)
+            {
+            if (medians1[i] >= medians2[i])
+                {
+                medians1[i] -= medians2[i];
+                medians2[i] = 0;
+                }
+            else
+                {
+                medians2[i] -= medians1[i];
+                medians1[i] = 0;
+                }
+            }
+        }
     geneInfo->medians1 = medians1;
     geneInfo->medians2 = medians2;
+
     }
 else
     {
     // TODO: compute median for single graph based on filtering of sample set
     }
 }
 
 static int gtexGeneItemHeight(struct track *tg, void *item);
 
 static void gtexGeneLoadItems(struct track *tg)
 /* Load method for track items */
 {
 /* Get track UI info */
 struct gtexGeneExtras *extras;
 AllocVar(extras);
 tg->extraUiData = extras;
 char *samples = cartUsualStringClosestToHome(cart, tg->tdb, FALSE, 
                                                 GTEX_SAMPLES, GTEX_SAMPLES_DEFAULT);
 extras->graphType = cloneString(samples);
 if (sameString(samples, GTEX_SAMPLES_COMPARE_SEX))
     extras->isComparison = TRUE;
+char *comparison = cartUsualStringClosestToHome(cart, tg->tdb, FALSE, GTEX_COMPARISON_DISPLAY,
+                        GTEX_COMPARISON_DEFAULT);
+extras->isDifference = sameString(comparison, GTEX_COMPARISON_DIFF) ? TRUE : FALSE;
 extras->maxMedian = gtexMaxMedianScore(NULL);
 
 /* Get geneModels in range */
 //TODO: version the table name, move to lib
 char *modelTable = "gtexGeneModel";
 struct hash *modelHash = loadGeneModels(modelTable);
 
 /* Get geneBeds (names and all-sample tissue median scores) in range */
 bedLoadItem(tg, tg->table, (ItemLoader)gtexGeneBedLoad);
 
 /* Create geneInfo items with BED and geneModels */
 struct gtexGeneInfo *geneInfo = NULL, *list = NULL;
 struct gtexGeneBed *geneBed = (struct gtexGeneBed *)tg->items;
 
 /* Load tissue colors: GTEx or rainbow */
@@ -746,39 +767,53 @@
 
 void gtexGeneDrawItems(struct track *tg, int seqStart, int seqEnd, 
                         struct hvGfx *hvg, int xOff, int yOff, int width, 
                         MgFont *font, Color color, enum trackVisibility vis)
 /* Draw GTEx gene graphs, which are of variable height so require custom layout in full
  * and pack modes */
 {
 if (vis == tvDense || vis == tvSquish)
     genericDrawItems(tg, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, vis);
 else if (vis == tvFull)
     gtexGeneDrawItemsFull(tg, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, vis);
 else if (vis == tvPack)
     genericDrawItems(tg, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, vis);
 }
 
+static char *tissueExpressionText(struct gtexTissue *tissue, double expScore, 
+                                        boolean doLogTransform, char *qualifier)
+/* Construct mouseover text for tissue graph */
+{
+static char buf[128];
+safef(buf, sizeof(buf), "%s (%.1f %s%s%sRPKM)", tissue->description, 
+                                doLogTransform ? log10(expScore+1.0) : expScore,
+                                qualifier != NULL ? qualifier : "",
+                                qualifier != NULL ? " " : "",
+                                doLogTransform ? "log " : "");
+return buf;
+}
+
 static void gtexGeneMapItem(struct track *tg, struct hvGfx *hvg, void *item, char *itemName, 
                         char *mapItemName, int start, int end, int x, int y, int width, int height)
 /* Create a map box for each tissue (bar in the graph) or a single map for squish/dense modes */
 {
 if (tg->visibility == tvDense || tg->visibility == tvSquish)
     {
     genericMapItem(tg, hvg, item, itemName, itemName, start, end, x, y, width, height);
     return;
     }
+struct gtexGeneExtras *extras = (struct gtexGeneExtras *)tg->extraUiData;
 struct gtexTissue *tissues = getTissues();
 struct gtexTissue *tissue = NULL;
 struct gtexGeneInfo *geneInfo = item;
 struct gtexGeneBed *geneBed = geneInfo->geneBed;
 int barWidth = gtexBarWidth();
 int padding = gtexGraphPadding();
 double maxMedian = ((struct gtexGeneExtras *)tg->extraUiData)->maxMedian;
 
 int graphX = gtexGraphX(geneBed);
 if (graphX < 0)
     return;
 // x1 is at left of graph
 int x1 = insideX + graphX;
 
 if (geneInfo->medians2)
@@ -789,41 +824,46 @@
 int i = 0;
 
 boolean doLogTransform = cartUsualBooleanClosestToHome(cart, tg->tdb, FALSE, GTEX_LOG_TRANSFORM, 
                                                 GTEX_LOG_TRANSFORM_DEFAULT);
 int topGraphHeight = gtexGeneGraphHeight(tg, geneInfo, doLogTransform, TRUE);
 topGraphHeight = max(topGraphHeight, tl.fontHeight);        // label
 int yZero = topGraphHeight + y - 1;  // yZero is bottom of (top) graph
 
 double viewMax = (double)cartUsualIntClosestToHome(cart, tg->tdb, FALSE, 
                                 GTEX_MAX_LIMIT, GTEX_MAX_LIMIT_DEFAULT);
 for (tissue = tissues; tissue != NULL; tissue = tissue->next, i++)
     {
     double expScore =  (geneInfo->medians1 ? geneInfo->medians1[i] : geneBed->expScores[i]);
     int height = valToClippedHeight(expScore, maxMedian, viewMax, 
                                         gtexMaxGraphHeight(), doLogTransform);
+    char *qualifier = NULL;
+    if (extras->isComparison && extras->isDifference)
+        qualifier = "F-M";
     mapBoxHc(hvg, start, end, x1, yZero-height, barWidth, height, tg->track, mapItemName,  
-                tissue->description);
+                tissueExpressionText(tissue, expScore, doLogTransform, qualifier));
     // add map box to comparison graph
     if (geneInfo->medians2)
         {
         double expScore = geneInfo->medians2[i];
         int height = valToClippedHeight(expScore, maxMedian, viewMax, 
                                         gtexMaxGraphHeight(), doLogTransform);
         int y = yZero + gtexGeneModelHeight() + gtexGeneMargin();  // y is top of bottom graph
+        if (extras->isComparison && extras->isDifference)
+            qualifier = "M-F";
         mapBoxHc(hvg, start, end, x1, y, barWidth, height, tg->track, mapItemName,
-                        tissue->description);
+                tissueExpressionText(tissue, expScore, doLogTransform, qualifier));
         }
     x1 = x1 + barWidth + padding;
     }
 }
 
 static char *gtexGeneItemName(struct track *tg, void *item)
 /* Return gene name */
 {
 struct gtexGeneInfo *geneInfo = (struct gtexGeneInfo *)item;
 struct gtexGeneBed *geneBed = geneInfo->geneBed;
 return geneBed->name;
 }
 
 static int gtexGeneHeight(void *item)
 {