8c929e5cd513a57db841da02fd0ba41310a76610 kate Wed Mar 22 14:57:03 2017 -0700 Add mouseovers. refs #18736 diff --git src/hg/hgTracks/barChartTrack.c src/hg/hgTracks/barChartTrack.c index 1acadff..cc046ec 100644 --- src/hg/hgTracks/barChartTrack.c +++ src/hg/hgTracks/barChartTrack.c @@ -1,36 +1,40 @@ /* barGraph tracks - display a colored bargraph across each region in a file */ /* Copyright (C) 2015 The Regents of the University of California * See README in this or parent directory for licensing information. */ #include "common.h" #include "hgTracks.h" #include "bed.h" #include "hvGfx.h" #include "spaceSaver.h" #include "barChartBed.h" #include "barChartCategory.h" #include "barChartUi.h" +// If a category contributes more than this percentage, its color is displayed in squish mode +// Could be a trackDb setting +#define SPECIFICITY_THRESHOLD 10 struct barChartTrack /* Track info */ { 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 barChartItem /* BED item plus computed values for display */ { struct barChartItem *next; /* Next in singly linked list */ struct barChartBed *bed; /* Item coords, name, exp count and values */ int height; /* Item height in pixels */ }; @@ -206,55 +210,55 @@ totalScore += expScore; } // threshold to consider this item category specific -- a category contributes > threshold % to // total level if (totalScore < 1 || maxScore <= (totalScore * threshold * .01)) return -1; return maxNum; } static Color barChartItemColor(struct track *tg, void *item, struct hvGfx *hvg) /* A bit of category-specific coloring in squish mode only, on bed item */ // TODO: Need a good function here to pick threshold from category count. // Also maybe trackDb setting { struct barChartBed *bed = (struct barChartBed *)item; -int threshold = 10; -int id = maxCategoryForItem(bed, threshold); +int id = maxCategoryForItem(bed, SPECIFICITY_THRESHOLD); if (id < 0) return MG_BLACK; struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; struct rgbColor color = extras->colors[id]; return hvGfxFindColorIx(hvg, color.r, color.g, color.b); } static void barChartLoadItems(struct track *tg) /* Load method for track items */ { if (tg->visibility == tvSquish || tg->limitedVis == tvSquish) tg->itemColor = barChartItemColor; /* Get track UI info */ struct barChartTrack *extras; AllocVar(extras); tg->extraUiData = extras; extras->doLogTransform = cartUsualBooleanClosestToHome(cart, tg->tdb, FALSE, BAR_CHART_LOG_TRANSFORM, BAR_CHART_LOG_TRANSFORM_DEFAULT); extras->maxMedian = barChartUiMaxMedianScore(); extras->noWhiteout = cartUsualBooleanClosestToHome(cart, tg->tdb, FALSE, BAR_CHART_NO_WHITEOUT, BAR_CHART_NO_WHITEOUT_DEFAULT); +extras->unit = trackDbSettingClosestToHomeOrDefault(tg->tdb, BAR_CHART_UNIT, ""); /* Get bed (names and all-sample category median scores) in range */ char *filter = getScoreFilterClause(cart, tg->tdb, NULL); bedLoadItemWhere(tg, tg->table, filter, (ItemLoader)barChartBedLoad); /* Create itemInfo items with BED and geneModels */ struct barChartItem *itemInfo = NULL, *list = NULL; struct barChartBed *bed = (struct barChartBed *)tg->items; /* Load category colors */ extras->colors = getCategoryColors(tg); filterCategories(tg); while (bed != NULL) @@ -278,39 +282,40 @@ #define MAX_GRAPH_HEIGHT 175 #define MAX_BAR_WIDTH 5 #define MAX_GRAPH_PADDING 2 #define WIN_MED_GRAPH 500000 #define MED_GRAPH_HEIGHT 100 #define MED_BAR_WIDTH 3 #define MED_GRAPH_PADDING 1 #define MIN_BAR_WIDTH 1 #define MIN_GRAPH_PADDING 0 #define MARGIN_WIDTH 1 -static int barChartBarWidth() +static int barChartBarWidth(struct track *tg) { long winSize = virtWinBaseCount; +int scale = (getCategoryCount(tg) < 15 ? 2 : 1); if (winSize < WIN_MAX_GRAPH) - return MAX_BAR_WIDTH; + return MAX_BAR_WIDTH * scale; else if (winSize < WIN_MED_GRAPH) - return MED_BAR_WIDTH; + return MED_BAR_WIDTH * scale; else - return MIN_BAR_WIDTH; + return MIN_BAR_WIDTH * scale; } static int barChartModelHeight(struct barChartTrack *extras) { return barChartBoxModelHeight()+3; } static int barChartPadding() { long winSize = virtWinBaseCount; if (winSize < WIN_MAX_GRAPH) return MAX_GRAPH_PADDING; else if (winSize < WIN_MED_GRAPH) return MED_GRAPH_PADDING; @@ -320,31 +325,31 @@ static int barChartMaxHeight() { long winSize = virtWinBaseCount; if (winSize < WIN_MAX_GRAPH) return MAX_GRAPH_HEIGHT; else if (winSize < WIN_MED_GRAPH) return MED_GRAPH_HEIGHT; else return tl.fontHeight * 4; } static int barChartWidth(struct track *tg, struct barChartItem *itemInfo) /* Width of bar chart in pixels */ { -int barWidth = barChartBarWidth(); +int barWidth = barChartBarWidth(tg); int padding = barChartPadding(); int count = filteredCategoryCount(tg); return (barWidth * count) + (padding * (count-1)) + 2; } static int barChartX(struct barChartBed *bed) /* Locate chart on X, relative to viewport. */ { int start = max(bed->chromStart, winStart); double scale = scaleForWindow(insideWidth, winStart, winEnd); int x1 = round((start - winStart) * scale); return x1; } static int barChartMargin() @@ -477,31 +482,31 @@ struct barChartBed *bed = (struct barChartBed *)itemInfo->bed; int topGraphHeight = barChartHeight(tg, itemInfo); topGraphHeight = max(topGraphHeight, tl.fontHeight); int yZero = topGraphHeight + y - 1; // yZero is bottom of graph int graphX = barChartX(bed); int x1 = xOff + graphX; // x1 is at left of graph int keepX = x1; drawGraphBase(tg, itemInfo, hvg, keepX, yZero+1); if (!extras->noWhiteout) drawGraphBox(tg, itemInfo, hvg, keepX, yZero+1); struct rgbColor lineColor = {.r=0}; int lineColorIx = hvGfxFindColorIx(hvg, lineColor.r, lineColor.g, lineColor.b); -int barWidth = barChartBarWidth(); +int barWidth = barChartBarWidth(tg); int graphPadding = barChartPadding(); char *colorScheme = cartUsualStringClosestToHome(cart, tg->tdb, FALSE, BAR_CHART_COLORS, BAR_CHART_COLORS_DEFAULT); Color clipColor = MG_MAGENTA; // draw bar graph double viewMax = (double)cartUsualIntClosestToHome(cart, tg->tdb, FALSE, BAR_CHART_MAX_LIMIT, BAR_CHART_MAX_LIMIT_DEFAULT); double maxMedian = ((struct barChartTrack *)tg->extraUiData)->maxMedian; int i; int expCount = bed->expCount; struct barChartCategory *categ; for (i=0, categ=extras->categories; i<expCount; i++, categ=categ->next) { if (!filterCategory(tg, categ->name)) @@ -563,158 +568,159 @@ } int topGraphHeight = barChartHeight(tg, itemInfo); topGraphHeight = max(topGraphHeight, tl.fontHeight); int bottomGraphHeight = 0; height = topGraphHeight + bottomGraphHeight + barChartMargin() + barChartModelHeight(extras); return height; } static int barChartItemHeight(struct track *tg, void *item) { int height = barChartItemHeightOptionalMax(tg, item, FALSE); return height; } -#ifdef LATER -static char *barChartText(struct barChartCategory *categ, double expScore, - boolean doLogTransform, char *qualifier) -/* Construct mouseover text for chart */ +static char *barChartMapText(struct track *tg, struct barChartCategory *categ, double expScore) +/* Construct mouseover text for a chart bar */ { static char buf[128]; -doLogTransform = FALSE; // for now, always display expression level on graph as raw RPKM -// TODO: Add units from trackDb -safef(buf, sizeof(buf), "%s (%.1f %s%s%s)", categ->label, - doLogTransform ? log10(expScore+1.0) : expScore, - qualifier != NULL ? qualifier : "", - qualifier != NULL ? " " : "", - doLogTransform ? "log " : ""); +struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; +safef(buf, sizeof(buf), "%s (%.1f %s)", categ->label, expScore, extras->unit); return buf; } -#endif static int barChartItemStart(struct track *tg, void *item) /* Return end chromosome coordinate of item, including graph */ { struct barChartItem *itemInfo = (struct barChartItem *)item; struct barChartBed *bed = (struct barChartBed *)itemInfo->bed; return bed->chromStart; } static int barChartItemEnd(struct track *tg, void *item) /* Return end chromosome coordinate of item, including graph */ { struct barChartItem *itemInfo = (struct barChartItem *)item; struct barChartBed *bed = (struct barChartBed *)itemInfo->bed; double scale = scaleForWindow(insideWidth, winStart, winEnd); int graphWidth = barChartWidth(tg, itemInfo); return max(bed->chromEnd, max(winStart, bed->chromStart) + graphWidth/scale); } -#ifdef MAP_ITEM - -// TODO: implement +static void getItemX(int start, int end, int *x1, int *x2) +/* Return startX, endX based on item coordinates and current window */ +// Residual (largely replaced by drawScaledBox -- still used by gene model bmap box +{ +int s = max(start, winStart); +int e = min(end, winEnd); +double scale = scaleForWindow(insideWidth, winStart, winEnd); +assert(x1); +*x1 = round((double)((int)s-winStart)*scale + insideX); +assert(x2); +*x2 = round((double)((int)e-winStart)*scale + insideX); +} static void barChartMapItem(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 on gene model and label, and one for each category (bar in the graph) in +/* Create a map box on item and label, and one for each category (bar in the graph) in * pack or full mode. Just single map for squish/dense modes */ { if (tg->limitedVis == tvDense) { genericMapItem(tg, hvg, item, itemName, itemName, start, end, x, y, width, height); return; } -struct barChartItebarChartItem *itemInfo = item; -struct barChartBed *bed = (struct barChartBed *)itemInfo->bed; struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; -int geneStart = bed->chromStart; -int geneEnd = bed->chromEnd; +struct barChartItem *itemInfo = (struct barChartItem *)item; +struct barChartBed *bed = (struct barChartBed *)itemInfo->bed; +int itemStart = bed->chromStart; +int itemEnd = bed->chromEnd; if (tg->limitedVis == tvSquish) { - int tisId = maxTissueForGene(bed); - char *maxTissue = ""; - if (tisId > 1) - maxTissue = getTissueDescription(tisId); + int categId = maxCategoryForItem(bed, SPECIFICITY_THRESHOLD); + char *maxCateg = ""; + if (categId > 1) + maxCateg = getCategoryLabel(tg, categId); char buf[128]; - safef(buf, sizeof buf, "%s %s", bed->name, maxTissue); + safef(buf, sizeof buf, "%s %s", bed->name, maxCateg); int x1, x2; - getItemX(geneStart, geneEnd, &x1, &x2); + getItemX(itemStart, itemEnd, &x1, &x2); int width = max(1, x2-x1); - mapBoxHc(hvg, geneStart, geneEnd, x1, y, width, height, + mapBoxHc(hvg, itemStart, itemEnd, x1, y, width, height, tg->track, mapItemName, buf); return; } int topGraphHeight = barChartHeight(tg, itemInfo); topGraphHeight = max(topGraphHeight, tl.fontHeight); // label int yZero = topGraphHeight + y - 1; // yZero is bottom of graph int x1 = insideX; - // add maps to category bars struct barChartCategory *categs = getCategories(tg); struct barChartCategory *categ = NULL; -int barWidth = barChartBarWidth(); +int barWidth = barChartBarWidth(tg); int padding = barChartPadding(); double maxMedian = ((struct barChartTrack *)tg->extraUiData)->maxMedian; int graphX = barChartX(bed); if (graphX < 0) return; // x1 is at left of graph x1 = insideX + graphX; double viewMax = (double)cartUsualIntClosestToHome(cart, tg->tdb, FALSE, BAR_CHART_MAX_LIMIT, BAR_CHART_MAX_LIMIT_DEFAULT); +int i = 0; for (categ = categs; categ != NULL; categ = categ->next, i++) { - if (!filterTissue(tg, categ->name)) + if (!filterCategory(tg, categ->name)) continue; double expScore = bed->expScores[i]; int height = valToClippedHeight(expScore, maxMedian, viewMax, barChartMaxHeight(), extras->doLogTransform); - char *qualifier = NULL; - mapBoxHc(hvg, geneStart, geneEnd, x1, yZero-height, barWidth, height, tg->track, mapItemName, - barChartText(categ, expScore, extras->doLogTransform, qualifier)); + mapBoxHc(hvg, itemStart, itemEnd, x1, yZero-height, barWidth, height, tg->track, mapItemName, + barChartMapText(tg, categ, expScore)); x1 = x1 + barWidth + padding; } -// add map boxes with description to gene model +#ifdef ITEM_NAME_MAP +// Maybe later +// add map boxes with item name to item if (itemInfo->geneModel && itemInfo->description) { // perhaps these are just start, end ? int itemStart = itemInfo->geneModel->txStart; int itemEnd = barChartItemEnd(tg, item); int x1, x2; getItemX(itemStart, itemEnd, &x1, &x2); int w = x2-x1; int labelWidth = mgFontStringWidth(tl.font, itemName); if (x1-labelWidth <= insideX) labelWidth = 0; // map over label int itemHeight = itemInfo->height; mapBoxHc(hvg, geneStart, geneEnd, x1-labelWidth, y, labelWidth, itemHeight-3, tg->track, mapItemName, itemInfo->description); // map over gene model (extending to end of item) int geneModelHeight = barChartModelHeight(extras); mapBoxHc(hvg, geneStart, geneEnd, x1, y+itemHeight-geneModelHeight-3, w, geneModelHeight, tg->track, mapItemName, itemInfo->description); } -} - #endif +} /* This is lifted nearly wholesale from gtexGene track. Could be shared */ static int getBarChartHeight(void *item) { struct barChartItem *itemInfo = (struct barChartItem *)item; assert(itemInfo->height != 0); return itemInfo->height; } /* This is lifted nearly wholesale from gtexGene track. Could be shared */ static int barChartTotalHeight(struct track *tg, enum trackVisibility vis) /* Figure out total height of track. Set in track and also return it */ { @@ -793,27 +799,26 @@ /* Return item name */ { struct barChartItem *chartItem = (struct barChartItem *)item; struct barChartBed *bed = (struct barChartBed *)chartItem->bed; return bed->name; } void barChartMethods(struct track *tg) /* Bar Chart track type: draw fixed width chart of colored bars over a BED item */ { bedMethods(tg); tg->canPack = TRUE; tg->drawItemAt = barChartDrawAt; tg->preDrawItems = barChartPreDrawItems; tg->loadItems = barChartLoadItems; -// TODO: restore when implemented; -//tg->mapItem = barChartMapItem; +tg->mapItem = barChartMapItem; tg->itemName = barChartItemName; tg->mapItemName = barChartItemName; tg->itemHeight = barChartItemHeight; tg->itemStart = barChartItemStart; tg->itemEnd = barChartItemEnd; tg->totalHeight = barChartTotalHeight; tg->nonPropDrawItemAt = barChartNonPropDrawAt; tg->nonPropPixelWidth = barChartNonPropPixelWidth; }