03a3d6b3671c719c86ca59b8ef6aab7d07b1a68d angie Mon Oct 8 11:04:37 2012 -0700 Improved mouseover titles for VCF hap-cluster display: trim indel left base,abbreviate long alleles (e.g. 40kb deletion was causing giant mouseover titles to be ignored/truncated by web browser). diff --git src/hg/hgTracks/vcfTrack.c src/hg/hgTracks/vcfTrack.c index 11156ce..b2c2f48 100644 --- src/hg/hgTracks/vcfTrack.c +++ src/hg/hgTracks/vcfTrack.c @@ -466,106 +466,123 @@ INLINE Color pgSnpColor(char *allele) /* Color allele by first base according to pgSnp palette. */ { if (allele[0] == 'A') return revCmplDisp ? MG_MAGENTA : MG_RED; else if (allele[0] == 'C') return revCmplDisp ? darkGreenColor : MG_BLUE; else if (allele[0] == 'G') return revCmplDisp ? MG_BLUE : darkGreenColor; else if (allele[0] == 'T') return revCmplDisp ? MG_RED : MG_MAGENTA; else return shadesOfGray[5]; } +INLINE void abbrevAndHandleRC(char *abbrevAl, size_t abbrevAlSize, const char *fullAl) +/* Limit the size of displayed allele to abbrevAlSize-1 and handle reverse-complemented display. */ +{ +boolean fullLen = strlen(fullAl); +boolean truncating = (fullLen > abbrevAlSize-1); +if (truncating) + { + int truncLen = abbrevAlSize - 4; + if (revCmplDisp) + { + safencpy(abbrevAl, abbrevAlSize, (fullAl + fullLen - truncLen), truncLen); + reverseComplement(abbrevAl, truncLen); + } + else + safencpy(abbrevAl, abbrevAlSize, fullAl, truncLen); + safecat(abbrevAl, abbrevAlSize, "..."); + } +else + { + safecpy(abbrevAl, abbrevAlSize, fullAl); + if (revCmplDisp) + reverseComplement(abbrevAl, fullLen); + } +} + INLINE void gtSummaryString(struct vcfRecord *rec, struct dyString *dy) // Make pgSnp-like mouseover text, but with genotype counts instead of allele counts. { if (isNotEmpty(rec->name) && !sameString(rec->name, ".")) dyStringPrintf(dy, "%s: ", rec->name); if (rec->alleleCount < 2) { dyStringAppendC(dy, '?'); return; } const struct vcfFile *vcff = rec->file; int gtRefRefCount = 0, gtRefAltCount = 0, gtAltAltCount = 0, gtUnkCount = 0, gtOtherCount = 0; int i; for (i=0; i < vcff->genotypeCount; i++) { struct vcfGenotype *gt = &(rec->genotypes[i]); if (gt->hapIxA == 0 && gt->hapIxB == 0) gtRefRefCount++; else if (gt->hapIxA == 1 && gt->hapIxB == 1) gtAltAltCount++; else if ((gt->hapIxA == 0 && gt->hapIxB == 1) || (gt->hapIxA == 1 && gt->hapIxB == 0)) gtRefAltCount++; else if (gt->hapIxA < 0 || gt->hapIxB < 0) gtUnkCount++; else gtOtherCount++; } -// These are pooled strings! Restore when done. -if (revCmplDisp) - { - for (i=0; i < rec->alleleCount; i++) - reverseComplement(rec->alleles[i], strlen(rec->alleles[i])); - } +char refAl[16]; +abbrevAndHandleRC(refAl, sizeof(refAl), rec->alleles[0]); +char altAl1[16]; +abbrevAndHandleRC(altAl1, sizeof(altAl1), rec->alleles[1]); if (sameString(chromName, "chrY")) dyStringPrintf(dy, "%s:%d %s:%d", - rec->alleles[0], gtRefRefCount, rec->alleles[1], gtRefAltCount); + refAl, gtRefRefCount, altAl1, gtRefAltCount); else - dyStringPrintf(dy, "%s/%s:%d %s/%s:%d %s/%s:%d", rec->alleles[0], rec->alleles[0], gtRefRefCount, - rec->alleles[0], rec->alleles[1], gtRefAltCount, - rec->alleles[1], rec->alleles[1], gtAltAltCount); + dyStringPrintf(dy, "%s/%s:%d %s/%s:%d %s/%s:%d", refAl, refAl, gtRefRefCount, + refAl, altAl1, gtRefAltCount, altAl1, altAl1, gtAltAltCount); if (gtUnkCount > 0) dyStringPrintf(dy, " unknown:%d", gtUnkCount); if (gtOtherCount > 0) dyStringPrintf(dy, " other:%d", gtOtherCount); -// Restore original values of pooled strings. -if (revCmplDisp) - { - for (i=0; i < rec->alleleCount; i++) - reverseComplement(rec->alleles[i], strlen(rec->alleles[i])); - } } void mapBoxForCenterVariant(struct vcfRecord *rec, struct hvGfx *hvg, struct track *tg, int xOff, int yOff, int width) /* Special mouseover for center variant */ { struct dyString *dy = dyStringNew(0); +unsigned int chromStartMap = vcfRecordTrimIndelLeftBase(rec); gtSummaryString(rec, dy); dyStringAppend(dy, " Haplotypes sorted on "); char *centerChrom = cartOptionalStringClosestToHome(cart, tg->tdb, FALSE, "centerVariantChrom"); if (centerChrom == NULL || !sameString(chromName, centerChrom)) dyStringAppend(dy, "middle variant by default. "); else dyStringAppend(dy, "this variant. "); dyStringAppend(dy, "To anchor sorting to a different variant, click on that variant and " "then click on the 'Use this variant' button below the variant name."); const double scale = scaleForPixels(width); int x1 = round((double)(rec->chromStart-winStart)*scale) + xOff; int x2 = round((double)(rec->chromEnd-winStart)*scale) + xOff; int w = x2-x1; if (w <= 1) { x1--; w = 3; } -mapBoxHgcOrHgGene(hvg, rec->chromStart, rec->chromEnd, x1, yOff, w, tg->height, tg->track, +mapBoxHgcOrHgGene(hvg, chromStartMap, rec->chromEnd, x1, yOff, w, tg->height, tg->track, rec->name, dy->string, NULL, TRUE, NULL); } // These are initialized when we start drawing, then constant. static Color purple = 0; static Color undefYellow = 0; enum hapColorMode { altOnlyMode, refAltMode, baseMode }; static Color colorByAltOnly(int refs, int alts, int unks) @@ -929,50 +946,54 @@ unsigned short gtHapIx = ((const struct hapCluster *)itemOrCluster)->gtHapIx; struct yFromNodeHelper *helper = extraData; if (gtHapIx >= helper->gtHapCount) errAbort("vcfTrack.c: gtHapIx %d out of range [0,%d).", gtHapIx, helper->gtHapCount); int y; if (yType == yrtStart) y = helper->gtHapIxToPxStart[gtHapIx]; else if (yType == yrtEnd) y = helper->gtHapIxToPxEnd[gtHapIx]; else y = (helper->gtHapIxToPxStart[gtHapIx] + helper->gtHapIxToPxEnd[gtHapIx]) / 2; return y; } void initTitleHelper(struct titleHelper *th, char *track, int startIx, int centerIx, int endIx, - int nRecords, const struct vcfFile *vcff) + int nRecords, struct vcfFile *vcff) /* Set up info including arrays of ref & alt alleles for cluster mouseover. */ { th->track = track; th->startIx = startIx; th->centerIx = centerIx; th->endIx = endIx; th->nRecords = nRecords; int len = endIx - startIx; AllocArray(th->refs, len); AllocArray(th->alts, len); struct vcfRecord *rec; int i; for (rec = vcff->records, i = 0; rec != NULL && i < endIx; rec = rec->next, i++) { if (i < startIx) continue; - th->refs[i-startIx] = rec->alleles[0]; - th->alts[i-startIx] = cloneString(rec->alleles[1]); - tolowers(th->alts[i-startIx]); + char refAl[16]; + abbrevAndHandleRC(refAl, sizeof(refAl), rec->alleles[0]); + th->refs[i-startIx] = vcfFilePooledStr(vcff, refAl); + char altAl1[16]; + abbrevAndHandleRC(altAl1, sizeof(altAl1), rec->alleles[1]); + tolowers(altAl1); + th->alts[i-startIx] = vcfFilePooledStr(vcff, altAl1); } } static void drawTreeInLabelArea(struct hacTree *ht, struct hvGfx *hvg, int yOff, int clipHeight, struct yFromNodeHelper *yHelper, struct titleHelper *titleHelper, boolean drawRectangle) /* Draw the haplotype clustering in the left label area (as much as fits there). */ { // Figure out which hvg to use, save current clipping, and clip to left label coords: struct hvGfx *hvgLL = (hvgSide != NULL) ? hvgSide : hvg; int clipXBak, clipYBak, clipWidthBak, clipHeightBak; hvGfxGetClip(hvgLL, &clipXBak, &clipYBak, &clipWidthBak, &clipHeightBak); hvGfxUnclip(hvgLL); hvGfxSetClip(hvgLL, leftLabelX, yOff, leftLabelWidth, clipHeight); // Draw the tree: @@ -999,31 +1020,31 @@ if (sameString(colorBy, VCF_HAP_COLORBY_ALTONLY)) colorMode = altOnlyMode; else if (sameString(colorBy, VCF_HAP_COLORBY_REFALT)) colorMode = refAltMode; else if (sameString(colorBy, VCF_HAP_COLORBY_BASE)) colorMode = baseMode; return colorMode; } static void vcfHapClusterDraw(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Split samples' chromosomes (haplotypes), cluster them by center-weighted * alpha similarity, and draw in the order determined by clustering. */ { -const struct vcfFile *vcff = tg->extraUiData; +struct vcfFile *vcff = tg->extraUiData; if (vcff->records == NULL) return; purple = hvGfxFindColorIx(hvg, 0x99, 0x00, 0xcc); undefYellow = hvGfxFindRgb(hvg, &undefinedYellowColor); enum hapColorMode colorMode = getColorMode(tg->tdb); pushWarnHandler(ignoreEm); struct vcfRecord *rec; for (rec = vcff->records; rec != NULL; rec = rec->next) vcfParseGenotypes(rec); popWarnHandler(); unsigned short gtHapCount = 0; int nRecords = slCount(vcff->records); int centerIx = getCenterVariantIx(tg, seqStart, seqEnd, vcff->records); // Limit the number of variants that we compare, to keep from timing out: // (really what we should limit is the number of distinct haplo's passed to hacTree!)