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