/* mafTrack - stuff to handle loading and display of * maf type tracks in browser. Mafs are multiple alignments. * This also handles axt's, mostly by convincing them they * are really mafs.... */ #include "common.h" #include "hash.h" #include "linefile.h" #include "jksql.h" #include "hdb.h" #include "hgTracks.h" #include "maf.h" #include "scoredRef.h" #include "hgMaf.h" #include "mafTrack.h" #include "customTrack.h" struct mafItem /* A maf track item. */ { struct mafItem *next; char *name; /* Common name */ char *db; /* Database */ int ix; /* Position in list. */ int height; /* Pixel height of item. */ }; static void mafItemFree(struct mafItem **pEl) /* Free up a mafItem. */ { struct mafItem *el = *pEl; if (el != NULL) { freeMem(el->name); freeMem(el->db); freez(pEl); } } void mafItemFreeList(struct mafItem **pList) /* Free a list of dynamically allocated mafItem's */ { struct mafItem *el, *next; for (el = *pList; el != NULL; el = next) { next = el->next; mafItemFree(&el); } *pList = NULL; } struct mafAli *mafOrAxtLoadInRegion(struct sqlConnection *conn, struct track *tg, char *chrom, int start, int end, boolean isAxt) /* Load mafs from region, either from maf or axt file. */ { if (isAxt) { struct hash *qSizeHash = hChromSizeHash(tg->otherDb); struct mafAli *mafList = axtLoadAsMafInRegion(conn, tg->table, chrom, start, end, database, tg->otherDb, hChromSize(database, chrom), qSizeHash); hashFree(&qSizeHash); return mafList; } else return mafLoadInRegion(conn, tg->table, chrom, start, end); } static struct mafItem *baseByBaseItems(struct track *tg, int scoreHeight) /* Make up base-by-base track items. */ { struct mafItem *miList = NULL, *mi; struct mafAli *maf; char *myOrg = hOrganism(database); char buf[64]; char *otherOrganism; /* Make up item that will show inserts in this organism. */ AllocVar(mi); mi->name = cloneString("Hidden Gaps"); mi->height = tl.fontHeight; slAddHead(&miList, mi); /* Make up item for this organism. */ AllocVar(mi); mi->name = myOrg; mi->height = tl.fontHeight; mi->db = cloneString(database); slAddHead(&miList, mi); /* Make up items for other organisms by scanning through * all mafs and looking at database prefix to source. */ { struct hash *hash = newHash(8); /* keyed by database. */ hashAdd(hash, mi->db, mi); /* Add in current organism. */ struct mafPriv *mp = getMafPriv(tg); for (maf = mp->list; maf != NULL; maf = maf->next) { struct mafComp *mc; for (mc = maf->components; mc != NULL; mc = mc->next) { mafSrcDb(mc->src, buf, sizeof(buf)); if (hashLookup(hash, buf) == NULL) { AllocVar(mi); mi->db = cloneString(buf); otherOrganism = hOrganism(mi->db); mi->name = (otherOrganism == NULL ? cloneString(buf) : otherOrganism); mi->height = tl.fontHeight; slAddHead(&miList, mi); hashAdd(hash, mi->db, mi); } } } hashFree(&hash); } /* Make up item that will show the score as grayscale. */ AllocVar(mi); mi->name = cloneString("Score"); mi->height = scoreHeight; slAddHead(&miList, mi); slReverse(&miList); return miList; } static struct mafItem *pairwiseItems(struct track *tg, int scoreHeight) /* Make up pairwise alignment items for each "other species" in the multiple alignment. */ { struct mafItem *miList = NULL, *mi; struct mafAli *maf; char buf[64]; char *otherOrganism; /* Make up item for wiggle */ AllocVar(mi); mi->name = cloneString("Score"); mi->height = scoreHeight; slAddHead(&miList, mi); if (trackDbSetting(tg->tdb, "pairwise") != NULL) /* Make up items for other organisms by scanning through * all mafs and looking at database prefix to source. */ { // TODO: share code with baseByBaseItems struct hash *hash = newHash(8); /* keyed by database. */ struct mafPriv *mp = getMafPriv(tg); for (maf = mp->list; maf != NULL; maf = maf->next) { struct mafComp *mc; boolean isMyOrg = TRUE; for (mc = maf->components; mc != NULL; mc = mc->next) { if (isMyOrg) { /* skip first maf component (this organism) */ isMyOrg = FALSE; continue; } mafSrcDb(mc->src, buf, sizeof(buf)); if (hashLookup(hash, buf) == NULL) { AllocVar(mi); mi->db = cloneString(buf); otherOrganism = hOrganism(mi->db); mi->name = (otherOrganism == NULL ? cloneString(buf) : otherOrganism); mi->height = tl.fontHeight; slAddHead(&miList, mi); hashAdd(hash, mi->db, mi); } } } hashFree(&hash); } slReverse(&miList); return miList; } static struct mafItem *mafItems(struct track *tg, int scoreHeight, boolean isBaseLevel, boolean isAxt) /* Load up items for full mode */ { struct mafAli *mafList = NULL; struct mafItem *miList = NULL; struct sqlConnection *conn = NULL; struct mafPriv *mp = getMafPriv(tg); if (mp->ct != NULL) errAbort("this maf path not supported for custom maf tracks"); else { conn = hAllocConn(database); /* Load up mafs and store in track so drawer doesn't have * to do it again. */ mafList = mafOrAxtLoadInRegion(conn, tg, chromName, winStart, winEnd, isAxt); hFreeConn(&conn); } mp->list = mafList; /* Make up tracks for display. */ if (isBaseLevel) miList = baseByBaseItems(tg, scoreHeight); else { miList = pairwiseItems(tg, scoreHeight); } return miList; } static void mafOrAxtLoad(struct track *tg, boolean isAxt) /* Load up maf or axt tracks. What this will do depends on * the zoom level and the display density. */ { struct mafItem *miList = NULL; int scoreHeight = tl.fontHeight; /* Create item list and set height depending * on display type. */ if (tg->visibility == tvFull) scoreHeight *= 4; if (zoomedToBaseLevel) { miList = mafItems(tg, scoreHeight, zoomedToBaseLevel, isAxt); } else { if (tg->visibility == tvFull && winBaseCount < MAF_SUMMARY_VIEW) { /* currently implemented only for medium zoom out */ miList = mafItems(tg, scoreHeight, FALSE, isAxt); } else { AllocVar(miList); miList->name = cloneString(tg->shortLabel); miList->height = scoreHeight; } } tg->items = miList; } static void mafLoad(struct track *tg) /* Load up maf tracks. */ { mafOrAxtLoad(tg, FALSE); } static int mafTotalHeight(struct track *tg, enum trackVisibility vis) /* Return total height of maf track. */ { struct mafItem *mi; int total = 0; for (mi = tg->items; mi != NULL; mi = mi->next) total += mi->height; tg->height = total; return tg->height; } static int mafItemHeight(struct track *tg, void *item) /* Return total height of maf track. */ { struct mafItem *mi = item; return mi->height; } static void mafFree(struct track *tg) /* Free up mafGroup items. */ { struct mafPriv *mp = getMafPriv(tg); mafAliFreeList((struct mafAli **)&mp->list); mafItemFreeList((struct mafItem **)&tg->items); } static char *mafName(struct track *tg, void *item) /* Return name of maf level track. */ { struct mafItem *mi = item; return mi->name; } static void processInserts(char *text, int textSize, char *insertLine, int baseCount) /* Make up insert line - it has a ' ' if there is no space * before char, and a '-' if there is. */ { int i, baseIx = 0; char c; for (i=0; i maxPos) { return 0; } return *(ixMafAli + position); } static void getNormalizedScores(struct mafAli *maf, char *masterText, double *scores, int scoreCount) /* Make an array of normalized scores, one per base. */ { int textIx, outIx = 0; double maxScore, minScore; double scoreScale; double score; mafColMinMaxScore(maf, &minScore, &maxScore); scoreScale = 1.0/(maxScore - minScore); for (textIx = 0; textIx < maf->textSize; ++textIx) { if (masterText[textIx] != '-') { score = (mafScoreRangeMultiz(maf, textIx, 1) - minScore)*scoreScale; if (score < 0.0) score = 0.0; if (score > 1.0) score = 1.0; scores[outIx] = score; ++outIx; } } } void mafFillInPixelScores(struct mafAli *maf, struct mafComp *mcMaster, double *scores, int numScores) /* Calculate one score per pixel normalized to be between 0.0 and 1.0. */ { int i,j; double score, minScore, maxScore, scoreScale; int textSize = maf->textSize; int masterSize = mcMaster->size; char *masterText = mcMaster->text; mafColMinMaxScore(maf, &minScore, &maxScore); if ((maxScore - minScore) < 0.0001) scoreScale = 0.0001; else scoreScale = 1.0/(maxScore - minScore); if (numScores >= masterSize) /* More pixels than bases */ { int x1,x2; int masterPos = 0; for (i=0; i 1.0) score = 1.0; x1 = masterPos*numScores/masterSize; x2 = (masterPos+1)*numScores/masterSize; /* make sure we don't overflow our array */ if (x2 > numScores - 1) x2 = numScores - 1; if (x1 < numScores) for (j=x1; j 1.0) score = 1.0; scores[i] = score; b1 = b2; t1 = t2; } } } static void mafDrawOverview(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw wiggle-plot based on overall maf scores rather than * computing them for sections. For this routine we don't * need to actually load the mafs, it's sufficient to load * the scoredRefs. */ { char **row; int rowOffset; struct sqlConnection *conn = hAllocConn(database); struct sqlResult *sr = hRangeQuery(conn, tg->table, chromName, seqStart, seqEnd, NULL, &rowOffset); double scale = scaleForPixels(width); int x1,x2,y,w; struct mafItem *mi = tg->items; int height1 = mi->height-2; while ((row = sqlNextRow(sr)) != NULL) { struct scoredRef ref; scoredRefStaticLoad(row + rowOffset, &ref); x1 = round((ref.chromStart - seqStart)*scale); x2 = round((ref.chromEnd - seqStart)*scale); w = x2-x1; if (w < 1) w = 1; if (vis == tvFull) { y = ref.score * height1; hvGfxBox(hvg, x1 + xOff, yOff + height1 - y, w, y+1, color); } else { int shade = ref.score * maxShade; Color c; if ((shade < 0) || (shade >= maxShade)) shade = 0; c = shadesOfGray[shade]; hvGfxBox(hvg, x1 + xOff, yOff, w, tg->heightPer, c); } } sqlFreeResult(&sr); hFreeConn(&conn); } void drawMafChain(struct hvGfx *hvg, int xOff, int yOff, int width, int height, boolean isDouble) /* draw single or double chain line between alignments in MAF display */ { int midY = yOff + (height>>1); int midY1 = midY - (height>>2); int midY2 = midY + (height>>2) - 1; Color gray = shadesOfGray[5]; midY--; /* tweaking end pixels, as done in chainTrack.c */ xOff--; /* this causes some lines to overwrite one * pixel of the previous box */ if (width == 1) /* width=1 lines won't draw via innerline, so widen them */ width++; width++; if (isDouble) { innerLine(hvg, xOff, midY1, width, gray); innerLine(hvg, xOff, midY2, width, gray); } else #ifdef MISSING_DATA { int x; Color fuzz = shadesOfGray[3]; /* hvGfxBox(hvg, xOff, yOff+height-5, width, 3, fuzz1); for (x = xOff+1; x < xOff+width; x += 2) { hvGfxBox(hvg, x, yOff+height-5, 1, 3, fuzz1); } */ for (x = xOff+1; x < xOff+width; x += 3) { hvGfxBox(hvg, x, yOff+height-5, 2, 3, fuzz); } } #else innerLine(hvg, xOff, midY, width, gray); #endif } void drawMafRegionDetails(struct mafAli *mafList, int height, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, Color altColor, enum trackVisibility vis, boolean isAxt, boolean chainBreaks) /* Draw wiggle/density plot based on scoring things on the fly * from list of MAF */ { struct mafAli *full, *sub = NULL, *maf = NULL; struct mafComp *mcMaster, *mc; char dbChrom[64]; int height1 = height-2; int ixMafAli = 0; /* alignment index, to allow alternating color */ int x1, x2; int lastAlignX2 = -1; int lastChainX2 = -1; double scale = scaleForPixels(width); safef(dbChrom, sizeof(dbChrom), "%s.%s", database, chromName); for (full = mafList; full != NULL; full = full->next) { double *pixelScores = NULL; int i; int w; sub = NULL; if (mafNeedSubset(full, dbChrom, seqStart, seqEnd)) sub = maf = mafSubset(full, dbChrom, seqStart, seqEnd); else maf = full; if (maf != NULL) { ixMafAli++; mcMaster = mafFindComponent(maf, dbChrom); mc = mcMaster->next; if (mc == NULL) { if (sub != NULL) mafAliFree(&sub); continue; } if (mcMaster->strand == '-') mafFlipStrand(maf); x1 = round((double)((int)mcMaster->start-seqStart-1)*scale) + xOff; x2 = round((double)((int)mcMaster->start-seqStart + mcMaster->size)*scale) + xOff; w = x2-x1+1; if (mc->size == 0) { /* suppress chain/alignment overlap */ if (x1 <= lastAlignX2) { int offset = lastAlignX2 - x1 + 1; x1 += offset; w -= offset; if (w <= 0) continue; } if (!chainBreaks) continue; lastChainX2 = x2+1; /* no alignment here -- just a gap/break annotation */ if ((mc->leftStatus == MAF_MISSING_STATUS ) && (mc->rightStatus == MAF_MISSING_STATUS)) { Color yellow = hvGfxFindRgb(hvg, &undefinedYellowColor); hvGfxBox(hvg, x1, yOff, w, height - 1, yellow); } else if ((mc->leftStatus == MAF_INSERT_STATUS || mc->leftStatus == MAF_NEW_NESTED_STATUS) || (mc->rightStatus == MAF_INSERT_STATUS || mc->rightStatus == MAF_NEW_NESTED_STATUS)) { /* double gap -> display double line ala chain tracks */ drawMafChain(hvg, x1, yOff, w, height, TRUE); } else if (isContigOrTandem( mc->leftStatus) || isContigOrTandem( mc->rightStatus)) { /* single gap -> display single line ala chain tracks */ drawMafChain(hvg, x1, yOff, w, height, FALSE); } } else { lastAlignX2 = x2; AllocArray(pixelScores, w); mafFillInPixelScores(maf, mcMaster, pixelScores, w); if (vis != tvFull && mc->leftStatus == MAF_NEW_STATUS) hvGfxBox(hvg, x1-3, yOff, 2, height, getChromBreakBlueColor()); for (i=0; i maxShade) shade = maxShade; c = shadesOfGray[shade]; //hvGfxBox(hvg, i+x1, yOff+2, 1, height - 5, c); hvGfxBox(hvg, i+x1, yOff, 1, height - 1, c); } } if (vis != tvFull && mc->leftStatus == MAF_NEW_NESTED_STATUS) { hvGfxBox(hvg, x1-1, yOff, 2, 1, getChromBreakGreenColor()); hvGfxBox(hvg, x1-1, yOff, 1, height, getChromBreakGreenColor()); hvGfxBox(hvg, x1-1, yOff + height-1, 2, 1, getChromBreakGreenColor()); } if (vis != tvFull && mc->rightStatus == MAF_NEW_NESTED_STATUS) { hvGfxBox(hvg, i+x1-1, yOff, 2, 1, getChromBreakGreenColor()); hvGfxBox(hvg, i+x1, yOff, 1, height, getChromBreakGreenColor()); hvGfxBox(hvg, i+x1-1, yOff + height-1, 2, 1, getChromBreakGreenColor()); } if (vis != tvFull && mc->rightStatus == MAF_NEW_STATUS) hvGfxBox(hvg, i+x1+1, yOff, 2, height, getChromBreakBlueColor()); freez(&pixelScores); } } if (sub != NULL) mafAliFree(&sub); } } static void mafDrawDetails(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis, boolean isAxt) /* Draw wiggle/density plot based on scoring things on the fly. */ { struct mafAli *mafList; struct sqlConnection *conn = hAllocConn(database); struct mafItem *miList = tg->items, *mi = miList; char *suffix; struct mafPriv *mp = getMafPriv(tg); mafList = mp->list; if (mafList == NULL) mafList = mafOrAxtLoadInRegion(conn, tg, chromName, seqStart, seqEnd, isAxt); /* display the multiple alignment in this region */ drawMafRegionDetails(mafList, mi->height, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, tg->ixAltColor, vis, isAxt, FALSE); mafAliFreeList(&mafList); yOff += mi->height + 1; if (vis == tvFull) { while ((mi = mi->next) != NULL) { /* construct pairwise table name for this organism */ /* if there's a value for the "pairwise" trackDb setting, use this * to construct the tablename, otherwise, use the track name */ char mafTable[64]; if ((suffix = trackDbSetting(tg->tdb, "pairwise")) == NULL || *suffix == 0) suffix = tg->table; safef(mafTable, sizeof(mafTable), "%s_%s", mi->name, suffix); if (!hTableExists(database, mafTable)) continue; mafList = mafLoadInRegion(conn, mafTable, chromName, seqStart, seqEnd); /* display pairwise alignments in this region in dense format */ drawMafRegionDetails(mafList, mi->height, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, tg->ixAltColor, tvDense, isAxt, FALSE); yOff += mi->height + 1; mafAliFreeList(&mafList); } } hFreeConn(&conn); } static void mafDrawGraphic(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis, boolean isAxt) /* Draw wiggle or density plot, not base-by-base. */ { int seqSize = seqEnd - seqStart; if (seqSize >= MAF_SUMMARY_VIEW) { mafDrawOverview(tg, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, vis); } else { mafDrawDetails(tg, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, vis, isAxt); } // density gradient of blastz's // mafDrawPairwise(tg, seqStart, seqEnd, hvg, xOff, yOff, font, width, color, vis); } static void mafDrawBases(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw base-by-base view. */ { struct mafItem *miList = tg->items, *mi; struct mafPriv *mp = getMafPriv(tg); struct mafAli *mafList = mp->list, *maf, *sub; int lineCount = slCount(miList); char **lines = NULL, *selfLine, *insertLine; double *scores; /* per base scores */ int *ixMafAli; /* per base alignment index */ double scoreScale; int i, y = yOff; struct dnaSeq *seq = NULL; struct hash *miHash = newHash(9); char dbChrom[64]; /* Allocate a line of characters for each item. */ AllocArray(lines, lineCount-1); lines[0] = needMem(winBaseCount+1); for (i=1; idna, winBaseCount); toUpperN(selfLine, winBaseCount); freeDnaSeq(&seq); /* Make hash of items keyed by database. */ i = 0; for (mi = miList; mi != NULL; mi = mi->next) { mi->ix = i++; if (mi->db != NULL) hashAdd(miHash, mi->db, mi); } /* Go through the mafs saving relevant info in lines. */ safef(dbChrom, sizeof(dbChrom), "%s.%s", database, chromName); i = 0; for (maf = mafList; maf != NULL; maf = maf->next) { sub = mafSubset(maf, dbChrom, winStart, winEnd); if (sub != NULL) { struct mafComp *mc, *mcMaster; char db[64]; int subStart,subEnd; int lineOffset, subSize; i++; mcMaster = mafFindComponent(sub, dbChrom); if (mcMaster->strand == '-') mafFlipStrand(sub); subStart = mcMaster->start; subEnd = subStart + mcMaster->size; subSize = subEnd - subStart; lineOffset = subStart - seqStart; for (mc = sub->components; mc != NULL; mc = mc->next) { mafSrcDb(mc->src, db, sizeof(db)); if (mc == mcMaster) { processInserts(mc->text, sub->textSize, insertLine+lineOffset, subSize); } else { mi = hashMustFindVal(miHash, db); processOtherSeq(mc->text, mcMaster->text, sub->textSize, lines[mi->ix] + lineOffset, subSize); } } getNormalizedScores(sub, mcMaster->text, scores + lineOffset, subSize); setIxMafAlign(i, ixMafAli + lineOffset, subSize); } mafAliFree(&sub); } /* Convert insert line from counts to characters. */ charifyInserts(insertLine, winBaseCount); for (mi = miList, i=0; mi->next != NULL; mi = mi->next, ++i) { char *line = lines[i]; int x = xOff; if (line == insertLine) x -= (width/winBaseCount)/2; spreadBasesString(hvg, x, y, width, mi->height-1, color, font, line, winBaseCount, FALSE); y += mi->height; } /* Draw score line. */ if (vis == tvDense) scoreScale = (maxShade); else scoreScale = (mi->height-2); for (i=0; iheight-1, color); } else { int wiggleH = scores[i] * scoreScale; hvGfxBox(hvg, x1+xOff, y + mi->height-2 - wiggleH, x2-x1, wiggleH+1, getIxMafAli(ixMafAli, i, winBaseCount) % 2 ? color : tg->ixAltColor); } } y += mi->height; /* Clean up */ for (i=0; iheight, tg->track, tg->track, NULL); } static void mafDraw(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw routine for mafAlign type tracks. This will load * the items as well as drawing them. */ { mafOrAxtDraw(tg,seqStart,seqEnd,hvg,xOff,yOff,width,font,color,vis,FALSE); } void mafMethods(struct track *tg) /* Make track group for maf multiple alignment. */ { tg->loadItems = mafLoad; tg->freeItems = mafFree; tg->drawItems = mafDraw; tg->itemName = mafName; tg->mapItemName = mafName; tg->totalHeight = mafTotalHeight; tg->itemHeight = mafItemHeight; tg->itemStart = tgItemNoStart; tg->itemEnd = tgItemNoEnd; tg->mapsSelf = TRUE; } static void axtLoad(struct track *tg) /* Load up axt tracks. What this will do depends on * the zoom level and the display density. */ { mafOrAxtLoad(tg, TRUE); } static void axtDraw(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw routine for axt type tracks. This will load * the items as well as drawing them. */ { mafOrAxtDraw(tg,seqStart,seqEnd,hvg,xOff,yOff,width,font,color,vis,TRUE); } void axtMethods(struct track *tg, char *otherDb) /* Make track group for axt alignments. */ { tg->otherDb = cloneString(otherDb); tg->loadItems = axtLoad; tg->freeItems = mafFree; tg->drawItems = axtDraw; tg->itemName = mafName; tg->mapItemName = mafName; tg->totalHeight = mafTotalHeight; tg->itemHeight = mafItemHeight; tg->itemStart = tgItemNoStart; tg->itemEnd = tgItemNoEnd; tg->mapsSelf = TRUE; }