d3f9b9ecf2a6d30bd8f97566f72e5650982c308d
kent
  Mon Dec 13 15:22:03 2021 -0800
Adding hgTracks display of merged bar charts.

diff --git src/hg/hgTracks/barChartTrack.c src/hg/hgTracks/barChartTrack.c
index f0a5d38..6885d9b 100644
--- src/hg/hgTracks/barChartTrack.c
+++ src/hg/hgTracks/barChartTrack.c
@@ -21,30 +21,31 @@
 struct barChartTrack
 /* Track extras */
     {
     boolean noWhiteout;         /* Suppress whiteout of graph background (allow highlight, blue lines) */
     double maxMedian;           /* Maximum median across all categories */
     boolean doLogTransform;     /* Log10(x+1) */
     char *unit;                /* Units for category values (e.g. RPKM) */
     struct barChartCategory *categories; /* Category names, colors, etc. */
     int categCount;             /* Count of categories - derived from above */
     char **categNames;          /* Category names  - derived from above */
     char **categLabels;         /* Category labels  - derived from above */
     struct rgbColor *colors;    /* Colors  for all categories */
     struct hash *categoryFilter; /* NULL out excluded factors */
     struct fieldedTable *statsTable;  /* Filled in if barChartStats setting is there */
     struct facetedTable *facetsTable;  /* Filled in if barChartFacets setting is there */
+    struct facetedTableMergedOffset *facetsMergeList; /* Filled in if barChartMerge setting is on */
     int maxViewLimit;
 
     // dimensions for drawing
     char *maxGraphSize;         /* optionally limit graph size (override semantic zoom)
                                      small, medium, or large (default) */
      int winMaxGraph;             /* Draw large graphs if window size smaller than this */
      int winMedGraph;             /* Draw medium graphs if window size greater than this 
                                         and smaller than winMaxGraph */
      int winSmallGraph;		/* Draw small graphs if windowSize between this and 
                                  * win medGraph, draw tiny if smaller */
 
     int squishHeight;           /* Height of item in squish mode (larger than typical) */
     int boxModelHeight;         /* Height of indicator box drawn under graph to show gene extent */
     int modelHeight;            /* Height of box drawn under graph with margin */
     int margin;			/* Added to pixel height to help separate things */
@@ -61,44 +62,46 @@
     {
     struct barChartItem *next;  /* Next in singly linked list */
     struct bed *bed;            /* Item coords, name, exp count and values */
     int height;                 /* Item height in pixels */
     };
 
 /***********************************************/
 /* Organize category info */
 
 struct barChartCategory *getCategories(struct track *tg)
 /* Get and cache category extras */
 {
 struct barChartTrack *extras;
 extras = (struct barChartTrack *)tg->extraUiData;
 if (extras->categories == NULL)
-    extras->categories = barChartUiGetCategories(database, tg->tdb);
+    {
+    extras->categories = barChartUiGetCategories(database, tg->tdb, extras->facetsMergeList);
+    }
 return extras->categories;
 }
 
 int getCategoryCount(struct track *tg)
 /* Get and cache the number of categories */
 {
 struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData;
 if (extras->categCount == 0)
     extras->categCount = slCount(getCategories(tg));
 return extras->categCount;
 }
 
-char *getCategoryName(struct track *tg, int id)
+static char *getCategoryName(struct track *tg, int id)
 /* Get category name from id, cacheing */
 {
 struct barChartCategory *categ;
 int count = getCategoryCount(tg);
 struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData;
 if (!extras->categNames)
     {
     struct barChartCategory *categs = getCategories(tg);
     AllocArray(extras->categNames, count);
     for (categ = categs; categ != NULL; categ = categ->next)
         extras->categNames[categ->id] = cloneString(categ->name);
     }
 if (id >= count)
     {
     warn("Bar chart track '%s': can't find sample ID %d in category file with %d categories. The category file is a two-column file where sample IDs are associated to categories, specified with the setting 'barChartSampleUrl'.\n", tg->shortLabel, id, count);
@@ -143,84 +146,104 @@
         }
     }
 return extras->colors;
 }
 
 static void fillInTables(struct track *tg, struct barChartTrack *extras)
 /* Fill in statTable and facetsTable on extras */
 {
 char *barChartStatsUrl = trackDbSetting(tg->tdb, "barChartStatsUrl");
 if (barChartStatsUrl != NULL)
     {
     extras->statsTable = fieldedTableFromTabFile(barChartStatsUrl,
 	    barChartStatsUrl, NULL, 0);
     char *barChartFacets = trackDbSetting(tg->tdb, "barChartFacets");
     if (barChartFacets != NULL)
+	{
 	extras->facetsTable = facetedTableFromTable(extras->statsTable, tg->track, barChartFacets);
+	if (trackDbSettingOn(tg->tdb, "barChartMerge"))
+	    {
+	    struct facetedTableMergedOffset *facetsMergeList = facetedTableMakeMergedOffsets(
+									extras->facetsTable, cart);
+	    assert(facetsMergeList != NULL);
+	    extras->facetsMergeList = facetsMergeList;
+	    }
+	}
     }
 }
 
 static void filterCategories(struct track *tg)
 /* Check cart for category selection.  NULL out unselected categorys in category list */
 {
 struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData;
+if (extras->facetsMergeList == NULL)
+    {
     struct barChartCategory *categ = NULL;
     extras->categories = getCategories(tg);
     extras->categoryFilter = hashNew(0);
     if (extras->facetsTable != NULL && extras->statsTable != NULL)
 	{
 	struct facetedTable *facTab = extras->facetsTable;
+	if (extras->facetsMergeList == NULL)
+	    {
 	    struct slInt *sel, *selList = facetedTableSelectOffsets(facTab, cart);
 	    for (sel = selList; sel != NULL; sel = sel->next)
 		{
 		char numBuf[16];
 		safef(numBuf, sizeof(numBuf), "%d", sel->val);
 		char *numCopy = lmCloneString(extras->categoryFilter->lm, numBuf);
 		hashAdd(extras->categoryFilter, numCopy, numCopy);
 		}
+	    }
 	return;
 	}
     else if (cartListVarExistsAnyLevel(cart, tg->tdb, FALSE, BAR_CHART_CATEGORY_SELECT))
 	{
 	struct slName *selectedValues = cartOptionalSlNameListClosestToHome(cart, tg->tdb, 
 							    FALSE, BAR_CHART_CATEGORY_SELECT);
 	if (selectedValues != NULL)
 	    {
 	    struct slName *name;
 	    for (name = selectedValues; name != NULL; name = name->next)
 		{
 		hashAdd(extras->categoryFilter, name->name, name->name);
 		}
 	    return;
 	    }
 	}
     /* no filter */
     for (categ = extras->categories; categ != NULL; categ = categ->next)
 	hashAdd(extras->categoryFilter, categ->name, categ->name);
     }
+}
 
 static int filteredCategoryCount(struct barChartTrack *extras)
 /* Count of categories to display */
 {
+if (extras->facetsMergeList != NULL)
+    return slCount(extras->facetsMergeList);
+else
     return hashNumEntries(extras->categoryFilter);
 }
 
 static boolean filterCategory(struct barChartTrack *extras, char *name)
 /* Does category pass filter */
 {
 if (name == NULL)
     return FALSE;
+else if (extras->facetsMergeList)
+    return TRUE;
 return (hashLookup(extras->categoryFilter, name) != NULL);
 }
 
 static int maxCategoryForItem(struct bed *bed)
 /* Return id of highest valued category for an item, if significantly higher than median.
  * If none are over threshold, return -1 */
 {
 double maxScore = 0.0, expScore;
 double totalScore = 0.0;
 int maxNum = 0, i;
 int expCount = bed->expCount;
 for (i=0; i<expCount; i++)
     {
     expScore = bed->expScores[i];
     if (expScore > maxScore)
@@ -392,47 +415,78 @@
 }
 
 static int chartWidth(struct track *tg, struct barChartItem *itemInfo)
 /* How wide is the chart? */
 {
 struct bed *bed = itemInfo->bed;
 int itemSize = windowsTotalIntersection(windows, bed->chrom, bed->chromStart, bed->chromEnd);
 int standardSize =  chartStandardWidth(tg, itemInfo);
 struct barChartTrack *extras = tg->extraUiData;
 if (extras->stretchToItem)
     return max(standardSize, itemSize);
 else
     return standardSize;
 }
 
+
+static void  mergeBedScores( struct facetedTableMergedOffset *facetsMergeList, 
+    struct bed *bedList)
+/* Return a merged down version of bed.  Bed serves as both input and output.  */
+/* Maybe a use for a nondestructive-to-inputs version will come up soon. This one is faster
+ * for the first use case though. */
+{
+int outSize = slCount(facetsMergeList);
+if (outSize > bedList->expCount)
+     {
+     errAbort("problem in mergeBedScores(): bed->expCount = %d, outSize = %d", 
+	bedList->expCount, outSize);
+     }
+float outBuf[outSize];
+struct bed *bed;
+for (bed = bedList; bed != NULL; bed = bed->next)
+    {
+    facetedTableMergeVals(facetsMergeList, bed->expScores, outBuf);
+    bed->expCount = outSize;
+    memcpy(bed->expScores, outBuf, sizeof(outBuf));
+    }
+}
+
 static void barChartLoadItems(struct track *tg)
 /* Load method for track items */
 {
 /* Initialize colors for visibilities that don't display actual barchart */
 if (tg->visibility == tvSquish || tg->limitedVis == tvSquish)
     tg->itemColor = barChartItemColor;
 tg->colorShades = shadesOfGray;
 
 /* Get track UI info */
 struct barChartTrack *extras;
 if (!tg->extraUiData)
     {
     AllocVar(extras);
     tg->extraUiData = extras;
     }
 extras = (struct barChartTrack *)tg->extraUiData;
 
+loadSimpleBedWithLoader(tg, (bedItemLoader)barChartSimpleBedLoad);
+fillInTables(tg, extras);
+struct bed *bedList = tg->items;
+if (extras->facetsMergeList)
+    {
+    mergeBedScores(extras->facetsMergeList, bedList);
+    }
+
 extras->colors = getCategoryColors(tg);
 
 struct trackDb *tdb = tg->tdb;
 extras->doLogTransform = barChartIsLogTransformed(cart, tdb->track, tdb);
 extras->maxMedian = barChartUiMaxMedianScore(tdb);
 extras->noWhiteout = cartUsualBooleanClosestToHome(cart, tdb, FALSE, 
                                                         BAR_CHART_NO_WHITEOUT, BAR_CHART_NO_WHITEOUT_DEFAULT);
 extras->maxViewLimit = barChartCurViewMax(cart, tg->track, tg->tdb);
 extras->maxGraphSize = trackDbSettingClosestToHomeOrDefault(tdb, 
                                 BAR_CHART_MAX_GRAPH_SIZE, BAR_CHART_MAX_GRAPH_SIZE_DEFAULT);
 extras->unit = trackDbSettingClosestToHomeOrDefault(tdb, BAR_CHART_UNIT, "");
 
 /* Set barchart dimensions to draw.  For three window sizes */
 
 #define MAX_BAR_CHART_MODEL_HEIGHT     2
@@ -461,50 +515,47 @@
 extras->winMaxGraph = WIN_MAX_GRAPH_DEFAULT;
 extras->winMedGraph = WIN_MED_GRAPH_DEFAULT;
 extras->winSmallGraph = WIN_SMALL_GRAPH_DEFAULT;
 char *setting = trackDbSetting(tdb, BAR_CHART_SIZE_WINDOWS);
 if (isNotEmpty(setting))
     {
     char *words[2];
     int ct = chopLine(setting, words);
     if (ct == 2)
         {
         extras->winMaxGraph = atoi(words[0]);
         extras->winMedGraph = atoi(words[1]);
         }
     }
 
-/* Get bed (names and all-sample category median scores) in range */
-loadSimpleBedWithLoader(tg, (bedItemLoader)barChartSimpleBedLoad);
 
 /* Create itemInfo items with BED and geneModels */
 struct barChartItem *itemInfo = NULL, *list = NULL;
-struct bed *bed = (struct bed *)tg->items;
+
+
+filterCategories(tg);
 
 /* Test that configuration matches data file */
-if (bed != NULL)
+if (bedList != NULL)
     {
     int categCount = getCategoryCount(tg);
-    int expCount = bed->expCount;
+    int expCount = bedList->expCount;
     if (categCount != expCount)
         warn("Bar chart track: category count mismatch between trackDb (%d) and data file (%d)",
                             categCount, expCount);
     }
 
-fillInTables(tg, extras);
-filterCategories(tg);
-
 int barCount = filteredCategoryCount(extras);
 double scale = 1.0;
 if (barCount <= 20)
     scale = 2.5;
 else if (barCount <= 40)
     scale = 1.6;
 else if (barCount <= 60)
     scale = 1.0;
 else if (barCount <= 120)
     scale = 0.8;
 else if (barCount <= 200)
     scale = 0.6;
 else 
     scale = 0.5;
 
@@ -538,44 +589,44 @@
     extras->padding = MIN_GRAPH_PADDING * scale;
     extras->maxHeight = tl.fontHeight * 4;
     }
 if (extras->barWidth > 1)
     extras->barWidth = floor(extras->barWidth);
     
 if (extras->barWidth <= 1 && extras->padding == 1)
    {
    extras->barWidth = 2;
    extras->padding = 0;
    }
 if (extras->barWidth < 1)
     extras->padding = 0;
 else
     extras->barWidth = round(extras->barWidth);
-// uglyAbort("barCount %d, graphSize %s, extras->barWidth = %g, extras->padding = %d, scale = %g", barCount, extras->maxGraphSize, extras->barWidth, extras->padding, scale);
 
 extras->modelHeight =  extras->boxModelHeight + 3;
 extras->margin = 1;
 extras->squishHeight = tl.fontHeight - tl.fontHeight/2;
 extras->stretchToItem = trackDbSettingOn(tg->tdb, "barChartStretchToItem");
 
-while (bed != NULL)
+struct bed *bed, *next;
+for (bed = bedList; bed != NULL; bed = next)
     {
+    next = bed->next;
+    bed->next = NULL;	/* Pop it off list */
     AllocVar(itemInfo);
-    itemInfo->bed = bed;
+    itemInfo->bed = (struct bed *)bed;
     slAddHead(&list, itemInfo);
-    bed = bed->next;
-    itemInfo->bed->next = NULL;
     itemInfo->height = barChartItemHeight(tg, itemInfo);
     }
 slReverse(&list);
 tg->items = list;
 }
 
 /***********************************************/
 /* Draw */
 
 static int barChartX(struct bed *bed)
 /* Locate chart on X, relative to viewport. */
 {
 // int start = max(bed->chromStart, winStart);	// Consider making this simply bed->chromStart -jk
 int start = bed->chromStart;
 double scale = scaleForWindow(insideWidth, winStart, winEnd);