5e5b469d59038d7717d302fd257dca1310b9d44a
kate
  Tue Nov 24 08:52:47 2015 -0800
Add tissue filter to GTEX gene track. refs #15645

diff --git src/hg/hgTracks/gtexTracks.c src/hg/hgTracks/gtexTracks.c
index f6fbbbc..5ed3184 100644
--- src/hg/hgTracks/gtexTracks.c
+++ src/hg/hgTracks/gtexTracks.c
@@ -16,30 +16,33 @@
 #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). */
     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 */
+    boolean doLogTransform;     /* Log10(x+1) */
+    struct gtexTissue *tissues; /* Cache tissue names, descriptions */
+    struct hash *tissueFilter;  /* For filter. NULL out excluded 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 */
     };
 
 /***********************************************/
@@ -237,30 +240,68 @@
                 }
             }
         }
     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 filterTissues(struct track *tg)
+/* Check cart for tissue selection.  NULL out unselected tissues in tissue list */
+{
+struct gtexGeneExtras *extras = (struct gtexGeneExtras *)tg->extraUiData;
+struct gtexTissue *tis = NULL;
+extras->tissues = getTissues();
+extras->tissueFilter = hashNew(0);
+if (cartListVarExistsAnyLevel(cart, tg->tdb, FALSE, GTEX_TISSUE_SELECT))
+    {
+    struct slName *selectedValues = cartOptionalSlNameListClosestToHome(cart, tg->tdb, 
+                                                        FALSE, GTEX_TISSUE_SELECT);
+    if (selectedValues != NULL)
+        {
+        struct slName *name;
+        for (name = selectedValues; name != NULL; name = name->next)
+            hashAdd(extras->tissueFilter, name->name, name->name);
+        return;
+        }
+    }
+/* no filter */
+for (tis = extras->tissues; tis != NULL; tis = tis->next)
+    hashAdd(extras->tissueFilter, tis->name, tis->name);
+}
+
+static int filteredTissueCount(struct track *tg)
+/* Count of tissues to display */
+{
+struct gtexGeneExtras *extras = (struct gtexGeneExtras *)tg->extraUiData;
+return hashNumEntries(extras->tissueFilter);
+}
+
+static boolean filterTissue(struct track *tg, char *name)
+/* Does tissue pass filter */
+{
+struct gtexGeneExtras *extras = (struct gtexGeneExtras *)tg->extraUiData;
+return (hashLookup(extras->tissueFilter, name) != NULL);
+}
+
 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;
@@ -278,30 +319,32 @@
 struct gtexGeneInfo *geneInfo = NULL, *list = NULL;
 struct gtexGeneBed *geneBed = (struct gtexGeneBed *)tg->items;
 
 /* Load tissue colors: GTEx or rainbow */
 char *colorScheme = cartUsualStringClosestToHome(cart, tg->tdb, FALSE, GTEX_COLORS, 
                         GTEX_COLORS_DEFAULT);
 if (sameString(colorScheme, GTEX_COLORS_GTEX))
     {
     extras->colors = getGtexTissueColors();
     }
 else
     {
     int expCount = geneBed->expCount;
     extras->colors = getRainbow(&saturatedRainbowAtPos, expCount);
     }
+filterTissues(tg);
+
 while (geneBed != NULL)
     {
     AllocVar(geneInfo);
     geneInfo->geneBed = geneBed;
     geneInfo->geneModel = hashFindVal(modelHash, geneBed->geneId);
     slAddHead(&list, geneInfo);
     geneBed = geneBed->next;
     geneInfo->geneBed->next = NULL;
     if (extras->isComparison && (tg->visibility == tvFull || tg->visibility == tvPack))
         // compute medians based on configuration (comparisons, and later, filters)
         loadComputedMedians(geneInfo, extras);
     geneInfo->height = gtexGeneItemHeight(tg, geneInfo);
     }
 slReverse(&list);
 tg->items = list;
@@ -361,37 +404,36 @@
 static int gtexMaxGraphHeight()
 {
 #ifdef MULTI_REGION
 int winSize = virtWinBaseCount; // GALT CHANGED OLD winEnd - winStart;
 #else
 int winSize = winEnd - winStart;
 #endif
 if (winSize < WIN_MAX_GRAPH)
     return MAX_GRAPH_HEIGHT;
 else if (winSize < WIN_MED_GRAPH)
     return MED_GRAPH_HEIGHT;
 else
     return MIN_GRAPH_HEIGHT;
 }
 
-static int gtexGraphWidth(struct gtexGeneInfo *geneInfo)
+static int gtexGraphWidth(struct track *tg, struct gtexGeneInfo *geneInfo)
 /* Width of GTEx graph in pixels */
 {
 int barWidth = gtexBarWidth();
 int padding = gtexGraphPadding();
-struct gtexGeneBed *geneBed = geneInfo->geneBed;
-int count = geneBed->expCount;
+int count = filteredTissueCount(tg);
 int labelWidth = geneInfo->medians2 ? tl.mWidth : 0;
 return (barWidth * count) + (padding * (count-1)) + labelWidth + 2;
 }
 
 static int gtexGraphX(struct gtexGeneBed *gtex)
 /* Locate graph on X, relative to viewport. Return -1 if it won't fit */
 {
 int start = max(gtex->chromStart, winStart);
 double scale = scaleForWindow(insideWidth, winStart, winEnd);
 int x1 = round((start - winStart) * scale);
 return x1;
 }
 
 static int gtexGeneModelHeight()
 {
@@ -421,35 +463,35 @@
 static int valToClippedHeight(double val, double maxVal, int maxView, int maxHeight, 
                                         boolean doLogTransform)
 /* Convert a value from 0 to maxVal to 0 to maxHeight-1, with clipping, or log transform the value */
 {
 double useVal = val;
 double useMax = maxVal;
 if (!doLogTransform)
     {
     useMax = maxView;
     if (val > maxView)
         useVal = maxView;
     }
 return valToHeight(useVal, useMax, gtexMaxGraphHeight(), doLogTransform);
 }
 
-static void drawGraphBase(struct hvGfx *hvg, int x, int y, struct gtexGeneInfo *geneInfo)
+static void drawGraphBase(struct track *tg, struct gtexGeneInfo *geneInfo, struct hvGfx *hvg, int x, int y)
 /* Draw faint line under graph to delineate extent when bars are missing (tissue w/ 0 expression) */
 {
 Color lightGray = MAKECOLOR_32(0xD1, 0xD1, 0xD1);
-int graphWidth = gtexGraphWidth(geneInfo);
+int graphWidth = gtexGraphWidth(tg, geneInfo);
 hvGfxBox(hvg, x, y, graphWidth, 1, lightGray);
 }
 
 static int gtexGeneGraphHeight(struct track *tg, struct gtexGeneInfo *geneInfo, 
                                 boolean doLogTransform, boolean doTop)
 /* Determine height in pixels of graph.  This will be the box for tissue with highest expression
    If doTop is false, compute height of bottom graph of comparison */
 {
 struct gtexGeneBed *geneBed = geneInfo->geneBed;
 int i;
 double maxExp = 0.0;
 int expCount = geneBed->expCount;
 double expScore;
 for (i=0; i<expCount; i++)
     {
@@ -482,62 +524,65 @@
     return;
     }
 
 int heightPer = tg->heightPer;
 int graphX = gtexGraphX(geneBed);
 if (graphX < 0)
     return;
 
 int topGraphHeight = gtexGeneGraphHeight(tg, geneInfo, doLogTransform, TRUE);
 topGraphHeight = max(topGraphHeight, tl.fontHeight);
 int yZero = topGraphHeight + y - 1;  // yZero is bottom of graph
 
 #ifndef MULTI_REGION
 int x1 = xOff + graphX;         // x1 is at left of graph
 int keepX = x1;                 // FIXME:  Too many X's!
-drawGraphBase(hvg, keepX, yZero+1, geneInfo);
+drawGraphBase(tg, geneInfo, hvg, keepX, yZero+1);
 
 int startX = x1;
 struct rgbColor lineColor = {.r=0};
 int lineColorIx = hvGfxFindColorIx(hvg, lineColor.r, lineColor.g, lineColor.b);
 int barWidth = gtexBarWidth();
 int graphPadding = gtexGraphPadding();
 char *colorScheme = cartUsualStringClosestToHome(cart, tg->tdb, FALSE, GTEX_COLORS, 
                         GTEX_COLORS_DEFAULT);
 Color labelColor = MG_GRAY;
 Color clipColor = MG_MAGENTA;
 
 // add labels to comparison graphs
 // TODO: generalize
 if (geneInfo->medians2)
     {
     hvGfxText(hvg, x1, yZero - tl.fontHeight + 1, labelColor, font, "F");
     hvGfxText(hvg, x1, yZero + gtexGeneModelHeight() + gtexGeneMargin() + 1, labelColor, font, "M");
     startX = startX + tl.mWidth+2;
     x1 = startX;
     }
 
 // draw bar graph
 // TODO: share this code with other graph
 double viewMax = (double)cartUsualIntClosestToHome(cart, tg->tdb, FALSE, 
                                 GTEX_MAX_LIMIT, GTEX_MAX_LIMIT_DEFAULT);
 double maxMedian = ((struct gtexGeneExtras *)tg->extraUiData)->maxMedian;
 int i;
 int expCount = geneBed->expCount;
 struct gtexGeneExtras *extras = (struct gtexGeneExtras *)tg->extraUiData;
-for (i=0; i<expCount; i++)
+struct gtexTissue *tis;
+for (i=0, tis=extras->tissues; i<expCount; i++, tis=tis->next)
     {
+    if (!filterTissue(tg, tis->name))
+        continue;
     struct rgbColor fillColor = extras->colors[i];
     if (barWidth == 1 && sameString(colorScheme, GTEX_COLORS_GTEX))
         {
         // brighten colors a bit so they'll be more visible at this scale
         fillColor = gtexTissueBrightenColor(fillColor);
         }
     int fillColorIx = hvGfxFindColorIx(hvg, fillColor.r, fillColor.g, fillColor.b);
     double expScore = (geneInfo->medians1 ? geneInfo->medians1[i] : geneBed->expScores[i]);
     int height = valToClippedHeight(expScore, maxMedian, viewMax, 
                                         gtexMaxGraphHeight(), doLogTransform);
     if (graphPadding == 0 || sameString(colorScheme, GTEX_COLORS_GTEX))
         hvGfxBox(hvg, x1, yZero-height+1, barWidth, height, fillColorIx);
     else
         hvGfxOutlinedBox(hvg, x1, yZero-height+1, barWidth, height, fillColorIx, lineColorIx);
     // mark clipped bar with magenta tip
@@ -550,34 +595,36 @@
 // draw gene model
 int yGene = yZero + gtexGeneMargin() - 1;
 tg->heightPer = gtexGeneModelHeight() + 1;
 struct linkedFeatures *lf = linkedFeaturesFromGenePred(tg, geneInfo->geneModel, FALSE);
 lf->filterColor = statusColor;
 linkedFeaturesDrawAt(tg, lf, hvg, xOff, yGene, scale, font, color, tvSquish);
 tg->heightPer = heightPer;
 
 if (!geneInfo->medians2)
     return;
 
 #ifndef MULTI_REGION
 // draw comparison bar graph (upside down)
 x1 = startX;
 yZero = yGene + gtexGeneModelHeight() + 1; // yZero is at top of graph
-drawGraphBase(hvg, keepX, yZero-1, geneInfo);
+drawGraphBase(tg, geneInfo, hvg, keepX, yZero-1);
 
-for (i=0; i<expCount; i++)
+for (i=0, tis=extras->tissues; i<expCount; i++, tis=tis->next)
     {
+    if (!filterTissue(tg, tis->name))
+        continue;
     struct rgbColor fillColor = extras->colors[i];
     if (barWidth == 1 && sameString(colorScheme, GTEX_COLORS_GTEX))
         {
         // brighten colors a bit so they'll be more visible at this scale
         struct hslColor hsl = mgRgbToHsl(fillColor);
         hsl.s = min(1000, hsl.s + 300);
         fillColor = mgHslToRgb(hsl);
         }
     int fillColorIx = hvGfxFindColorIx(hvg, fillColor.r, fillColor.g, fillColor.b);
     double expScore = geneInfo->medians2[i];
     int height = valToClippedHeight(expScore, maxMedian, viewMax, gtexMaxGraphHeight(), 
                                         doLogTransform);
     if (graphPadding == 0 || sameString(colorScheme, GTEX_COLORS_GTEX))
         hvGfxBox(hvg, x1, yZero, barWidth, height, fillColorIx);
     else
@@ -821,30 +868,32 @@
     // skip over labels in comparison graphs
     x1 = x1 + tl.mWidth+ 2;
     }
 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++)
     {
+    if (!filterTissue(tg, tissue->name))
+        continue;
     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,  
                 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
@@ -906,31 +955,32 @@
 
 static int gtexGeneItemStart(struct track *tg, void *item)
 /* Return end chromosome coordinate of item, including graph */
 {
 struct gtexGeneInfo *geneInfo = (struct gtexGeneInfo *)item;
 struct gtexGeneBed *geneBed = geneInfo->geneBed;
 return geneBed->chromStart;
 }
 
 static int gtexGeneItemEnd(struct track *tg, void *item)
 /* Return end chromosome coordinate of item, including graph */
 {
 struct gtexGeneInfo *geneInfo = (struct gtexGeneInfo *)item;
 struct gtexGeneBed *geneBed = geneInfo->geneBed;
 double scale = scaleForWindow(insideWidth, winStart, winEnd);
-int graphWidth = gtexGraphWidth(geneInfo);
+int graphWidth = gtexGraphWidth(tg, geneInfo);
+int x;
 return max(geneBed->chromEnd, max(winStart, geneBed->chromStart) + graphWidth/scale);
 }
 
 void gtexGeneMethods(struct track *tg)
 {
 tg->drawItems = gtexGeneDrawItems;
 tg->drawItemAt = gtexGeneDrawAt;
 tg->loadItems = gtexGeneLoadItems;
 //tg->freeItems = gtexGeneFreeItems;
 tg->mapItem = gtexGeneMapItem;
 tg->itemName = gtexGeneItemName;
 tg->mapItemName = gtexGeneItemName;
 tg->itemHeight = gtexGeneItemHeight;
 tg->itemStart = gtexGeneItemStart;
 tg->itemEnd = gtexGeneItemEnd;