5b1a4883cd3c9d4be801592648d729a394e07240 kate Mon Jul 10 15:31:42 2017 -0700 Add indicator of tissue color to eQTL right label for single-tissue items. refs #15646 diff --git src/hg/hgTracks/gtexEqtlClusterTrack.c src/hg/hgTracks/gtexEqtlClusterTrack.c index a2b800b..8305e7a 100644 --- src/hg/hgTracks/gtexEqtlClusterTrack.c +++ src/hg/hgTracks/gtexEqtlClusterTrack.c @@ -15,52 +15,59 @@ /* Track info for filtering (maybe later for draw, map) */ { struct gtexTissue *tissues; /* Tissue names, descriptions */ struct hash *tissueHash; /* Tissue info, keyed by UCSC name, filtered by UI */ double minEffect; /* Effect size filter (abs value) */ double minProb; /* Probability filter */ }; static struct gtexEqtlCluster *loadOne(char **row) /* Load up gtexEqtlCluster from array of strings. */ { return gtexEqtlClusterLoad(row); } static boolean filterTissues(struct track *track, struct gtexEqtlClusterTrack *extras) -/* Check cart for tissue selection */ +/* Check cart for tissue selection. Populate track tissues hash */ { char *version = gtexVersionSuffix(track->table); extras->tissues = gtexGetTissues(version); extras->tissueHash = hashNew(0); -if (cartListVarExistsAnyLevel(cart, track->tdb, FALSE, GTEX_TISSUE_SELECT)) - { +struct gtexTissue *tis = NULL; +for (tis = extras->tissues; tis != NULL; tis = tis->next) + hashAdd(extras->tissueHash, tis->name, tis); + +// if all tissues included, return full hash +if (!cartListVarExistsAnyLevel(cart, track->tdb, FALSE, GTEX_TISSUE_SELECT)) + return FALSE; + +// create tissue hash with only included tissues +struct hash *tisHash = hashNew(0); struct slName *selectedValues = cartOptionalSlNameListClosestToHome(cart, track->tdb, FALSE, GTEX_TISSUE_SELECT); - if (selectedValues != NULL) - { +if (selectedValues == NULL) + return FALSE; + struct slName *name; for (name = selectedValues; name != NULL; name = name->next) - hashAdd(extras->tissueHash, name->name, name->name); - return TRUE; - } + { + tis = (struct gtexTissue *)hashFindVal(extras->tissueHash, name->name); + if (tis != NULL) + hashAdd(tisHash, name->name, tis); } -/* no filter */ -struct gtexTissue *tis = NULL; -for (tis = extras->tissues; tis != NULL; tis = tis->next) - hashAdd(extras->tissueHash, tis->name, tis->name); -return FALSE; +extras->tissueHash = tisHash; +return TRUE; } static void excludeTissue(struct gtexEqtlCluster *eqtl, int i) /* Mark the tissue to exclude from display */ { eqtl->expScores[i] = 0.0; } static boolean isExcludedTissue(struct gtexEqtlCluster *eqtl, int i) /* Check if eQTL is excluded */ { return (eqtl->expScores[i] == 0.0); } static boolean eqtlIncludeFilter(struct track *track, void *item) @@ -123,49 +130,92 @@ boolean hasTissueFilter = filterTissues(track, extras); if (!hasTissueFilter && extras->minEffect == 0.0 && extras->minProb == 0.0) return; // more filtering filterItems(track, eqtlIncludeFilter, "include"); } static char *gtexEqtlClusterItemName(struct track *track, void *item) /* Left label is gene name */ { struct gtexEqtlCluster *eqtl = (struct gtexEqtlCluster *)item; return eqtl->target; } -static char *gtexEqtlClusterSourcesLabel(struct track *track, void *item) -/* Right label is tissue (or number of tissues if >1) */ +static int itemTissueCount(void *item) +/* Return count of non-excluded tissues in the item */ { struct gtexEqtlCluster *eqtl = (struct gtexEqtlCluster *)item; -int i, included; -for (i=0, included=0; iexpCount; i++) +int included = 0; +int i; +for (i=0; iexpCount; i++) if (!isExcludedTissue(eqtl, i)) included++; -if (included == 1) - return eqtl->expNames[i-1]; +return included; +} + +static int itemTissueIndex(void *item) +/* Return index of first non-excluded tissue in an item. Used for single-tissue items. */ +{ +struct gtexEqtlCluster *eqtl = (struct gtexEqtlCluster *)item; +int i; +for (i=0; iexpCount; i++) + if (!isExcludedTissue(eqtl, i)) + return i; +return -1; +} + +static char *itemSourcesLabel(void *item) +/* Right label is tissue (or number of tissues if >1) */ +{ +struct gtexEqtlCluster *eqtl = (struct gtexEqtlCluster *)item; +int ct = itemTissueCount(item); +if (ct == 1) + { + int i = itemTissueIndex(item); + if (i<0) + errAbort("GTEx eQTL %s/%s track tissue index is negative", eqtl->name, eqtl->target); + return eqtl->expNames[i]; + } struct dyString *ds = dyStringNew(0); -dyStringPrintf(ds, "%d tissues", included); +dyStringPrintf(ds, "%d tissues", ct); return dyStringCannibalize(&ds); } +static struct rgbColor itemTissueColor(struct track *track, void *item) +/* Return tissue color for single-tissue item, or NULL if none found */ +{ +int i = itemTissueIndex(item); +assert(i>=0); +struct gtexEqtlClusterTrack *extras = (struct gtexEqtlClusterTrack *)track->extraUiData; +struct gtexEqtlCluster *eqtl = (struct gtexEqtlCluster *)item; +struct gtexTissue *tis = (struct gtexTissue *)hashFindVal(extras->tissueHash, eqtl->expNames[i]); +assert (tis); +return (struct rgbColor){.r=COLOR_32_BLUE(tis->color), .g=COLOR_32_GREEN(tis->color), + .b=COLOR_32_RED(tis->color)}; +} + +#define TISSUE_COLOR_DOT "*" + static int gtexEqtlClusterItemRightPixels(struct track *track, void *item) /* Return number of pixels we need to the right of an item (for sources label). */ { -return mgFontStringWidth(tl.font, gtexEqtlClusterSourcesLabel(track, item)); +int ret = mgFontStringWidth(tl.font, itemSourcesLabel(item)); +if (itemTissueCount(item) == 1) + ret += mgFontStringWidth(tl.font, TISSUE_COLOR_DOT); +return ret; } static Color gtexEqtlClusterItemColor(struct track *track, void *item, struct hvGfx *hvg) /* Color by highest effect in list (blue -, red +), with brighter for higher effect (teal, fuschia) */ { struct gtexEqtlCluster *eqtl = (struct gtexEqtlCluster *)item; double maxEffect = 0.0; int i; for (i=0; iexpCount; i++) { if (isExcludedTissue(eqtl, i)) continue; double effect = eqtl->expScores[i]; if (fabs(effect) > fabs(maxEffect)) maxEffect = effect; @@ -185,33 +235,43 @@ } static void gtexEqtlClusterDrawItemAt(struct track *track, void *item, struct hvGfx *hvg, int xOff, int y, double scale, MgFont *font, Color color, enum trackVisibility vis) /* Draw GTEx eQTL cluster with right label indicating source(s) */ { bedPlusLabelDrawAt(track, item, hvg, xOff, y, scale, font, color, vis); if (vis != tvFull && vis != tvPack) return; /* Draw text to the right */ struct gtexEqtlCluster *eqtl = (struct gtexEqtlCluster *)item; int x2 = round((double)((int)eqtl->chromEnd-winStart)*scale) + xOff; int x = x2 + tl.mWidth/2; -char *label = gtexEqtlClusterSourcesLabel(track, item); +char *label = itemSourcesLabel(item); int w = mgFontStringWidth(font, label); hvGfxTextCentered(hvg, x, y, w, track->heightPer, MG_BLACK, font, label); +if (itemTissueCount(item) == 1) + { + // append asterisk in tissue color + struct rgbColor tisColor = itemTissueColor(track, item); + x += w; + w = mgFontStringWidth(font, TISSUE_COLOR_DOT); + int ix = hvGfxFindColorIx(hvg, tisColor.r, tisColor.g, tisColor.b); + font = mgFontForSizeAndStyle(tl.textSize, "bold"); + hvGfxTextCentered(hvg, x, y, w, track->heightPer, ix, font, TISSUE_COLOR_DOT); + } } static void gtexEqtlClusterMapItem(struct track *track, 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 item and label with list of tissues with colors and effect size */ { char *title = itemName; if (track->limitedVis != tvDense) { struct gtexEqtlCluster *eqtl = (struct gtexEqtlCluster *)item; // Experiment: construct list of tissues with colors and effect sizes for mouseover //struct gtexEqtlClusterTrack *extras = (struct gtexEqtlClusterTrack *)track->extraUiData; //struct hash *tissueHash = extras->tissueHash; struct dyString *ds = dyStringNew(0);