3c529301c2900f3b03ea89955426db75b9b631f9 angie Mon Feb 7 10:08:54 2011 -0800 Track #1684 (SNPs 132 (dbSNP)): Added coloring by allele frequency:items are shaded on a scale of red (rare) to blue (common), or black when no frequencies were given. Now I'm wondering if I should have used a log scale, but this works so I'm checking it in. diff --git src/hg/hgTracks/variation.c src/hg/hgTracks/variation.c index 1754ee6..0ebe7ba 100644 --- src/hg/hgTracks/variation.c +++ src/hg/hgTracks/variation.c @@ -25,37 +25,30 @@ static boolean snp125ClassFilterOn = FALSE; static boolean snp125MolTypeFilterOn = FALSE; static boolean snp125ValidFilterOn = FALSE; static boolean snp125FuncFilterOn = FALSE; static boolean snp132ExceptionFilterOn = FALSE; static boolean snp132BitfieldFilterOn = FALSE; static struct slName *snp125LocTypeFilter = NULL; static struct slName *snp125ClassFilter = NULL; static struct slName *snp125MolTypeFilter = NULL; static struct slName *snp125ValidFilter = NULL; static struct slName *snp125FuncFilter = NULL; static struct slName *snp132ExceptionFilter = NULL; static struct slName *snp132BitfieldFilter = NULL; -void filterSnpMapItems(struct track *tg, boolean (*filter) - (struct track *tg, void *item)) -/* Filter out items from track->itemList. */ -{ -filterSnpItems(tg, filter); -} - void filterSnpItems(struct track *tg, boolean (*filter) (struct track *tg, void *item)) /* Filter out items from track->itemList. */ { struct slList *newList = NULL, *el, *next; for (el = tg->items; el != NULL; el = next) { next = el->next; if (filter(tg, el)) slAddHead(&newList, el); } slReverse(&newList); tg->items = newList; } @@ -356,30 +349,41 @@ snp125MolTypeFilterItem(el) && snp125ClassFilterItem(el) && snp125ValidFilterItem(el) && snp125FuncFilterItem(el) && (version >= 128 || snp125LocTypeFilterItem(el)) && (version < 132 || (snp132MinSubmittersFilterItem(el) && snp132ExceptionFilterItem(el) && snp132BitfieldFilterItem(el)))) slAddHead(&newList, el); } slReverse(&newList); tg->items = newList; } +struct orthoBed +/* Abbreviated version of orthoAlleles: bed4 plus chimp allele */ + { + struct orthoBed *next; /* Next in singly linked list. */ + char *chrom; /* Human chromosome or FPC contig */ + unsigned chromStart; /* Start position in chromosome */ + unsigned chromEnd; /* End position in chromosome */ + char *name; /* Name of item */ + char *chimp; /* Chimp allele */ + }; + struct orthoBed *orthoBedLoad(char **row) /* Load a bed from row fetched with select * from bed * from database. Dispose of this with bedFree(). */ /* This should be moved to kent/src/hg/lib. */ { struct orthoBed *ret; if (sameString(row[10], "?")) return NULL; AllocVar(ret); ret->chrom = cloneString(row[0]); ret->chromStart = sqlUnsigned(row[1]); ret->chromEnd = sqlUnsigned(row[2]); ret->name = cloneString(row[3]); ret->chimp = cloneString(row[10]); return ret; @@ -465,35 +469,78 @@ continue; } /* update the snp->name with the ortho data */ dyStringPrintf(extra, "%s %s>%s", snpItem->name, orthoItem->chimp, snpItem->observed); snpItem->name = cloneString(extra->string); dyStringClear(extra); /* increment the list pointers */ snpItem = snpItem->next; orthoItem = orthoItem->next; } freeDyString(&extra); sqlFreeResult(&sr); hFreeConn(&conn); } -Color snp125Color(struct track *tg, void *item, struct hvGfx *hvg) -/* Return color of snp track item -- which we stash in the weight column (only the bed4 - * fields of snp are used at draw time). */ +static float snp132MajorAlleleFreq(const struct snp132Ext *snp) +/* Some SNPs have >2 alleles, so minor allele frequency is harder to define. + * So instead, I'm using major allele frequency -- (1 - major) can be a proxy for minor. */ { -struct snp125 *snp = item; +float majorAlF = 0.0; +int i; +for (i = 0; i < snp->alleleFreqCount; i++) + if (snp->alleleFreqs[i] > majorAlF) + majorAlF = snp->alleleFreqs[i]; +return majorAlF; +} + +static Color snp132ColorByAlleleFreq(struct snp132Ext *snp, struct hvGfx *hvg) +/* If snp has allele freq data, return a shade from red (rare) to blue (common); + * otherwise return black. */ +{ +static boolean colorsInited = FALSE; +static Color redToBlue[EXPR_DATA_SHADES]; +static struct rgbColor red = {255, 0, 0}; +static struct rgbColor blue = {0, 0, 255}; +if (!colorsInited) + hvGfxMakeColorGradient(hvg, &red, &blue, EXPR_DATA_SHADES, redToBlue); +if (snp->alleleFreqCount > 0) + { + float majorAlF = snp132MajorAlleleFreq(snp); + // >2 common alleles (e.g. at VNTR sites) can cause low major allele freq; + // cap at 0.5 to avoid overflow in the shade calculation. + if (majorAlF < 0.5) + majorAlF = 0.5; + if (majorAlF > 1.0) + majorAlF = 1.0; + // Shade on a scale of 100% (red) to 50% (blue): + int shadeIndex = (int)((1.0 - 2.0*(majorAlF - 0.5)) * (EXPR_DATA_SHADES-1)); + return redToBlue[shadeIndex]; + } +return MG_BLACK; +} + +Color snp125Color(struct track *tg, void *item, struct hvGfx *hvg) +/* Return color of snp track item -- stashed in the weight column for set/enum + * attributes that were used for sorting at draw time. Allele frequency shading + * must be done at draw time because it uses hvg. Aside from allele frequencies + * and overloaded weight, only the bed4 fields of snp are used at draw time). */ +{ +struct snp132Ext *snp = item; +if (snp125ColorSource == snp125ColorSourceAlleleFreq) + return snp132ColorByAlleleFreq(snp, hvg); +else return (Color)(snp->weight); } int snp125ColorCmpRaw(const Color ca, const char *aName, const Color cb, const char *bName) /* Compare to sort based on color -- black first, red last. This is not * a slSort Cmp function, just a comparator of the values. */ { /* order is important here */ if (ca==cb) return 0; if (ca==MG_RED) return 1; if (cb==MG_RED) return -1; if (ca==MG_GREEN) @@ -518,36 +565,31 @@ int snp125ColorCmp(const void *va, const void *vb) /* Compare to sort based on color -- black first, red last */ { const struct snp125 *a = *((struct snp125 **)va); const struct snp125 *b = *((struct snp125 **)vb); const Color ca = (Color)(a->weight); const Color cb = (Color)(b->weight); return snp125ColorCmpRaw(ca, a->name, cb, b->name); } int snp125ColorCmpDesc(const void *va, const void *vb) /* Compare to sort based on color -- red first, black last */ { -const struct snp125 *a = *((struct snp125 **)va); -const struct snp125 *b = *((struct snp125 **)vb); -const Color ca = (Color)(a->weight); -const Color cb = (Color)(b->weight); - -return snp125ColorCmpRaw(cb, b->name, ca, a->name); +return snp125ColorCmp(vb, va); } static enum snp125Color *snp125ColorsFromCart(char *track, char *attribute, char *vars[], boolean varsAreOld, char *defaults[], int varCount) /* Look up attribute colors in cart using both old and new cart var names where applicable. */ { enum snp125Color *cartColors = NULL; AllocArray(cartColors, varCount); int i; for (i=0; i < varCount; i++) { char cartVar[512]; safef(cartVar, sizeof(cartVar), "%s.%s%s", track, attribute, (varsAreOld ? snp125OldColorVarToNew(vars[i], attribute) : vars[i])); char *defaultCol = defaults[i]; @@ -575,37 +617,31 @@ defaultMaxWeight = atoi(setting); snp125WeightCutoff = cartUsualInt(cart, cartVar, // Check old cart var name and tdb default: cartUsualInt(cart, "snp125WeightCutoff", defaultMaxWeight)); safef(cartVar, sizeof(cartVar), "%s.minSubmitters", track); snp132MinSubmitters = cartUsualInt(cart, cartVar, SNP132_DEFAULT_MIN_SUBMITTERS); snp125MolTypeFilter = snp125FilterFromCart(cart, track, "molType", &snp125MolTypeFilterOn); snp125ClassFilter = snp125FilterFromCart(cart, track, "class", &snp125ClassFilterOn); snp125ValidFilter = snp125FilterFromCart(cart, track, "valid", &snp125ValidFilterOn); snp125FuncFilter = snp125FilterFromCart(cart, track, "func", &snp125FuncFilterOn); snp125LocTypeFilter = snp125FilterFromCart(cart, track, "locType", &snp125LocTypeFilterOn); snp132ExceptionFilter = snp125FilterFromCart(cart, track, "exceptions", &snp132ExceptionFilterOn); snp132BitfieldFilter = snp125FilterFromCart(cart, track, "bitfields", &snp132BitfieldFilterOn); -safef(cartVar, sizeof(cartVar), "%s.colorSource", track); -char *snp125ColorSourceDefault = snp125ColorSourceLabels[SNP125_DEFAULT_COLOR_SOURCE]; -char *colorSourceCart = cartUsualString(cart, cartVar, - cartUsualString(cart, snp125ColorSourceOldVar, - snp125ColorSourceDefault)); -snp125ColorSource = stringArrayIx(colorSourceCart, snp125ColorSourceLabels, - snp125ColorSourceArraySize); +snp125ColorSource = snp125ColorSourceFromCart(cart, tdb); snp125MolTypeCart = snp125ColorsFromCart(track, "molType", snp125MolTypeOldColorVars, TRUE, snp125MolTypeDefault, snp125MolTypeArraySize); snp125ClassCart = snp125ColorsFromCart(track, "class", snp125ClassOldColorVars, TRUE, snp125ClassDefault, snp125ClassArraySize); snp125ValidCart = snp125ColorsFromCart(track, "valid", snp125ValidOldColorVars, TRUE, snp125ValidDefault, snp125ValidArraySize); snp125LocTypeCart = snp125ColorsFromCart(track, "locType", snp125LocTypeOldColorVars, TRUE, snp125LocTypeDefault, snp125LocTypeArraySize); snp132ExceptionsCart = snp125ColorsFromCart(track, "exception", snp132ExceptionVarName, FALSE, snp132ExceptionDefault, snp132ExceptionArraySize); snp132BitfieldsCart = snp125ColorsFromCart(track, "bitfield", snp132BitfieldVarName, FALSE, snp132BitfieldDefault, snp132BitfieldArraySize); snp125FuncCartColorHash = hashNew(0); snp125FuncCartNameHash = hashNew(0); @@ -654,31 +690,33 @@ break; case snp125ColorBlue: return MG_BLUE; break; case snp125ColorGray: return MG_GRAY; break; case snp125ColorBlack: default: return MG_BLACK; break; } } static void snp125ColorItems(struct track *tg, int version) -/* Use cart settings and snp properties to assign a color to snp -- and stash it in snp->weight. */ +/* Use cart settings and snp properties to assign a color to snp -- and stash it in snp->weight. + * Note: we can't do the allele frequency shading here because that uses hvg for the shades, + * and hvg isn't passed in until draw time. */ { struct snp132Ext *snp; for (snp = tg->items; snp != NULL; snp = snp->next) { enum snp125Color color = snp125ColorBlack; int valIx; char *words[128]; int wordCount, i; char buf[4096]; switch (snp125ColorSource) { case snp125ColorSourceMolType: valIx = stringArrayIx(snp->molType, snp125MolTypeDataName, snp125MolTypeArraySize); if (valIx < 0) valIx = 0; @@ -740,103 +778,147 @@ else { safecpy(buf, sizeof(buf), snp->bitfields); wordCount = chopCommas(buf, words); for (i = 0; i < wordCount; i++) { valIx = stringArrayIx(words[i], snp132BitfieldDataName, snp132BitfieldArraySize); enum snp125Color wordColor = snp132BitfieldsCart[valIx]; if (snp125ColorCmpRaw(snp125ColorToMg(wordColor), "wordColor", snp125ColorToMg(color), "color") > 0) color = wordColor; } } } break; + case snp125ColorSourceAlleleFreq: default: color = snp125ColorBlack; break; } snp->weight = snp125ColorToMg(color); } } static void loadSnp125Basic(struct track *tg, int version, void *(loadFunction)(char **)) /* load snp125 or snp132Ext items from table */ { struct sqlConnection *conn = hAllocConn(database); struct snp132Ext *itemList = NULL; int rowOffset = 0; char **row = NULL; struct sqlResult *sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd, NULL, &rowOffset); while ((row = sqlNextRow(sr)) != NULL) { struct slList *snp = loadFunction(row + rowOffset); slAddHead(&itemList, snp); } sqlFreeResult(&sr); hFreeConn(&conn); tg->items = itemList; } +static int bedCmpStartName(const void *va, const void *vb) +/* Compare two bed4's by chromStart (chrom assumed to be same) and name. */ +{ +const struct bed *a = *((struct bed **)va); +const struct bed *b = *((struct bed **)vb); +int diff = a->chromStart - b->chromStart; +if (diff == 0) + diff = strcmp(a->name, b->name); +return diff; +} + +static int snp132AlFreqCmp(const void *va, const void *vb) +/* Compare two SNPs by allele frequency: (no info/)rare first, common last. */ +{ +const struct snp132Ext *a = *((struct snp132Ext **)va); +const struct snp132Ext *b = *((struct snp132Ext **)vb); +if (a->alleleFreqCount == 0 && b->alleleFreqCount == 0) + return bedCmpStartName(va, vb); +else if (a->alleleFreqCount == 0 && b->alleleFreqCount != 0) + return -1; +else if (a->alleleFreqCount == 0 && b->alleleFreqCount != 0) + return 1; +else + { + float majorAlFA = snp132MajorAlleleFreq(a); + float majorAlFB = snp132MajorAlleleFreq(b); + if (majorAlFA > majorAlFB) + return -1; + else if (majorAlFA < majorAlFB) + return 1; + else + return bedCmpStartName(va, vb); + } +} + +static int snp132AlFreqCmpDesc(const void *va, const void *vb) +/* Compare two SNPs by allele frequency: common first, (no info/)rare last. */ +{ +return snp132AlFreqCmp(vb, va); +} + void loadSnp125(struct track *tg) /* load snps from table, ortho alleles from snpXXXOrthoXXX table, filter and color. */ { int version = snpVersion(tg->table); if (version >= 132) loadSnp125Basic(tg, version, (void *)(snp132ExtLoad)); else if (version >= 125) loadSnp125Basic(tg, version, (void *)(snp125Load)); else errAbort("How was loadSnp125 called on version < 125? (%d)", version); snp125SetupFiltersAndColorsFromCart(tg->tdb); filterSnp125Items(tg, version); snp125ColorItems(tg, version); +// If in dense or squish mode, sort items by color or allele frequency: +boolean sortByAF = (snp125ColorSource == snp125ColorSourceAlleleFreq); if (tg->visibility == tvDense) - slSort(&tg->items, snp125ColorCmp); + slSort(&tg->items, sortByAF ? snp132AlFreqCmp : snp125ColorCmp); else if (tg->visibility == tvSquish) - slSort(&tg->items, snp125ColorCmpDesc); + slSort(&tg->items, sortByAF ? snp132AlFreqCmpDesc : snp125ColorCmpDesc); else { slSort(&tg->items, snpOrthoCmp); setSnp125ExtendedNameExtra(tg); enum trackVisibility newVis = limitVisibility(tg); if (newVis == tvDense) - slSort(&tg->items, snp125ColorCmp); + slSort(&tg->items, sortByAF ? snp132AlFreqCmp : snp125ColorCmp); else if (newVis == tvSquish) - slSort(&tg->items, snp125ColorCmpDesc); + slSort(&tg->items, sortByAF ? snp132AlFreqCmpDesc : snp125ColorCmpDesc); } } void loadSnpMap(struct track *tg) /* Load up snpMap from database table to track items. */ { int snpMapSource, snpMapType; for (snpMapSource=0; snpMapSource<snpMapSourceCartSize; snpMapSource++) snpMapSourceCart[snpMapSource] = cartUsualString(cart, snpMapSourceStrings[snpMapSource], snpMapSourceDefault[snpMapSource]); for (snpMapType=0; snpMapType<snpMapTypeCartSize; snpMapType++) snpMapTypeCart[snpMapType] = cartUsualString(cart, snpMapTypeStrings[snpMapType], snpMapTypeDefault[snpMapType]); bedLoadItem(tg, "snpMap", (ItemLoader)snpMapLoad); if (!startsWith("hg",database)) return; -filterSnpMapItems(tg, snpMapSourceFilterItem); -filterSnpMapItems(tg, snpMapTypeFilterItem); +filterSnpItems(tg, snpMapSourceFilterItem); +filterSnpItems(tg, snpMapTypeFilterItem); } void loadSnp(struct track *tg) /* Load up snp from database table to track items. */ { int i = 0; snpAvHetCutoff = atof(cartUsualString(cart, "snpAvHetCutoff", "0.0")); for (i=0; i < snpSourceCartSize; i++) snpSourceCart[i] = cartUsualString(cart, snpSourceStrings[i], snpSourceDefault[i]); for (i=0; i < snpMolTypeCartSize; i++) snpMolTypeCart[i] = cartUsualString(cart, snpMolTypeStrings[i], snpMolTypeDefault[i]); for (i=0; i < snpClassCartSize; i++) snpClassCart[i] = cartUsualString(cart, snpClassStrings[i], snpClassDefault[i]); @@ -1007,31 +1089,31 @@ tg->track, tg->mapItemName(tg, s), NULL); } void snp125DrawItemAt(struct track *tg, void *item, struct hvGfx *hvg, int xOff, int y, double scale, MgFont *font, Color color, enum trackVisibility vis) /* Draw a single snp125 item at position. */ { struct snp125 *s = item; int heightPer = tg->heightPer; int x1 = round((double)((int)s->chromStart-winStart)*scale) + xOff; int x2 = round((double)((int)s->chromEnd-winStart)*scale) + xOff; int w = x2-x1; if (w < 1) w = 1; -hvGfxBox(hvg, x1, y, w, heightPer, (Color)(s->weight)); +hvGfxBox(hvg, x1, y, w, heightPer, color); /* Clip here so that text will tend to be more visible... */ if (tg->drawName && vis != tvSquish) mapBoxHc(hvg, s->chromStart, s->chromEnd, x1, y, w, heightPer, tg->track, tg->mapItemName(tg, s), NULL); } static void snpMapDrawItems(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw snpMap items. */ { double scale = scaleForPixels(width); int lineHeight = tg->lineHeight; int heightPer = tg->heightPer; int w, y; @@ -1095,30 +1177,31 @@ y += lineHeight; } } } static void snpDrawItems(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw snp items. */ { double scale = scaleForPixels(width); int lineHeight = tg->lineHeight; int heightPer = tg->heightPer; int y, w; boolean withLabels = (withLeftLabels && vis == tvPack && !tg->drawName); +snp125ColorSource = snp125ColorSourceFromCart(cart, tg->tdb); if (!tg->drawItemAt) errAbort("missing drawItemAt in track %s", tg->track); if (vis == tvPack || vis == tvSquish) { struct spaceSaver *ss = tg->ss; struct spaceNode *sn = NULL; hvGfxSetClip(hvg, insideX, yOff, insideWidth, tg->height); for (sn = ss->nodeList; sn != NULL; sn = sn->next) { struct slList *item = sn->val; int s = tg->itemStart(tg, item); int e = tg->itemEnd(tg, item); int x1 = round((s - winStart)*scale) + xOff; int x2 = round((e - winStart)*scale) + xOff;