/* wigMafTrack - display multiple alignment files with score wiggle * and base-level alignment, or else density plot of pairwise alignments * (zoomed out) */ #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 "wiggle.h" #include "hCommon.h" #include "hgMaf.h" #include "mafTrack.h" #include "customTrack.h" #include "mafSummary.h" #include "mafFrames.h" #include "phyloTree.h" #define GAP_ITEM_LABEL "Gaps" #define MAX_SP_SIZE 2000 struct wigMafItem /* A maf track item -- * a line of bases (base level) or pairwise density gradient (zoomed out). */ { struct wigMafItem *next; char *name; /* Common name */ char *db; /* Database */ int group; /* number of species group/clade */ int ix; /* Position in list. */ int height; /* Pixel height of item. */ int inserts[128]; int insertsSize; int seqEnds[128]; int seqEndsSize; int brackStarts[128]; int brackStartsSize; int brackEnds[128]; int brackEndsSize; }; static void wigMafItemFree(struct wigMafItem **pEl) /* Free up a wigMafItem. */ { struct wigMafItem *el = *pEl; if (el != NULL) { freeMem(el->name); freeMem(el->db); freez(pEl); } } void wigMafItemFreeList(struct wigMafItem **pList) /* Free a list of dynamically allocated wigMafItem's */ { struct wigMafItem *el, *next; for (el = *pList; el != NULL; el = next) { next = el->next; wigMafItemFree(&el); } *pList = NULL; } Color wigMafItemLabelColor(struct track *tg, void *item, struct hvGfx *hvg) /* Return color to draw a maf item based on the species group it is in */ { struct wigMafItem *mi = (struct wigMafItem *)item; if (sameString(mi->name, GAP_ITEM_LABEL)) return getOrangeColor(); return (((struct wigMafItem *)item)->group % 2 ? tg->ixAltColor : tg->ixColor); } static struct mafAli *wigMafLoadInRegion(struct sqlConnection *conn, struct sqlConnection *conn2, char *table, char *chrom, int start, int end, char *file) /* Load mafs from region */ { return mafLoadInRegion2(conn, conn2, table, chrom, start, end, file); } static struct wigMafItem *newMafItem(char *s, int g, boolean lowerFirstChar) /* Allocate and initialize a maf item. Species param can be a db or name */ { struct wigMafItem *mi; char *val; AllocVar(mi); if ((val = hGenome(s)) != NULL) { /* it's a database name */ mi->db = cloneString(s); mi->name = val; } else { mi->db = cloneString(s); mi->name = cloneString(s); } mi->name = hgDirForOrg(mi->name); if (lowerFirstChar) *mi->name = tolower(*mi->name); mi->height = tl.fontHeight; mi->group = g; return mi; } struct wigMafItem *getSpeciesFromMaf(struct track *track, int height) { struct wigMafItem *mi = NULL, *miList = NULL; struct hash *hash = newHash(8); /* keyed by database. */ struct mafPriv *mp = getMafPriv(track); struct mafAli *maf; char buf[64]; char *otherOrganism; if (mp->list == (struct mafAli *)-1) return NULL; 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 (sameString(buf, database)) continue; 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); return miList; } struct wigMafItem *newSpeciesItems(struct track *track, int height) /* Make up item list for all species configured in track settings */ { struct dyString *order = dyStringNew(256); char option[MAX_SP_SIZE]; char *species[MAX_SP_SIZE]; char *groups[20]; char *defaultOff[MAX_SP_SIZE]; char sGroup[24]; struct wigMafItem *mi = NULL, *miList = NULL; int group; int i; int speciesCt = 0, groupCt = 1; int speciesOffCt = 0; struct hash *speciesOffHash = newHash(0); #define BRANEY_SAYS_USETARG_IS_OBSOLETE #ifndef BRANEY_SAYS_USETARG_IS_OBSOLETE char *speciesTarget = trackDbSetting(track->tdb, SPECIES_TARGET_VAR); char *speciesTree = trackDbSetting(track->tdb, SPECIES_TREE_VAR); bool useTarg; /* use phyloTree to find shortest path */ struct phyloTree *tree = NULL; #endif///ndef BRANEY_SAYS_USETARG_IS_OBSOLETE char *speciesUseFile = trackDbSetting(track->tdb, SPECIES_USE_FILE); char *msaTable = NULL; /* either speciesOrder or speciesGroup is specified in trackDb */ char *speciesOrder = trackDbSetting(track->tdb, SPECIES_ORDER_VAR); char *speciesGroup = trackDbSetting(track->tdb, SPECIES_GROUP_VAR); char *speciesOff = trackDbSetting(track->tdb, SPECIES_DEFAULT_OFF_VAR); bool lowerFirstChar = TRUE; char *firstCase; firstCase = trackDbSetting(track->tdb, ITEM_FIRST_CHAR_CASE); if (firstCase != NULL) { if (sameWord(firstCase, "noChange")) lowerFirstChar = FALSE; } #ifndef BRANEY_SAYS_USETARG_IS_OBSOLETE char buffer[128]; // According to Tim, this makes no sense as ".vis" safef(buffer, sizeof(buffer), "%s.vis",track->track); if (!cartVarExists(cart, buffer) && (speciesTarget != NULL)) useTarg = TRUE; else { char *val; val = cartUsualString(cart, buffer, "useCheck"); useTarg = sameString("useTarg",val); } if (useTarg && (tree = phyloParseString(speciesTree)) == NULL) useTarg = FALSE; #endif///ndef BRANEY_SAYS_USETARG_IS_OBSOLETE if (speciesOrder == NULL && speciesGroup == NULL && speciesUseFile == NULL) return getSpeciesFromMaf(track, height); if (speciesGroup) groupCt = chopLine(cloneString(speciesGroup), groups); if (speciesUseFile) { if ((speciesGroup != NULL) || (speciesOrder != NULL)) errAbort("Can't specify speciesUseFile and speciesGroup or speciesOrder"); if (hIsGsidServer()) { msaTable = trackDbSetting(track->tdb, "msaTable"); if (msaTable != NULL) { speciesOrder = cartGetOrderFromFileAndMsaTable(database, cart, speciesUseFile, msaTable); } else { speciesOrder = cartGetOrderFromFile(database, cart, speciesUseFile); } } else { speciesOrder = cartGetOrderFromFile(database, cart, speciesUseFile); } speciesOff = NULL; } /* keep track of species configured off initially for track */ if (speciesOff) { speciesOffCt = chopLine(cloneString(speciesOff), defaultOff); for (i = 0; i < speciesOffCt; i++) hashAdd(speciesOffHash, defaultOff[i], NULL); } char *prefix = track->track; // use when setting things to the cart if (tdbIsContainerChild(track->tdb)) prefix = tdbGetContainer(track->tdb)->track; /* Make up items for other organisms by scanning through group & species track settings */ for (group = 0; group < groupCt; group++) { if (groupCt != 1 || !speciesOrder) { safef(sGroup, sizeof sGroup, "%s%s", SPECIES_GROUP_PREFIX, groups[group]); speciesOrder = trackDbRequiredSetting(track->tdb, sGroup); } #ifndef BRANEY_SAYS_USETARG_IS_OBSOLETE if (useTarg) { warn("BRANEY_SAYS useTarg should never be TRUE!"); char *ptr, *path; struct hash *orgHash = newHash(0); int numNodes, ii; char *nodeNames[512]; char *species = NULL; char *lowerString; path = phyloNodeNames(tree); numNodes = chopLine(path, nodeNames); for(ii=0; ii < numNodes; ii++) { if ((ptr = hOrganism(nodeNames[ii])) != NULL) { ptr[0] = tolower(ptr[0]); hashAdd(orgHash, ptr, nodeNames[ii]); } else { hashAdd(orgHash, nodeNames[ii], nodeNames[ii]); } } lowerString = cartUsualString(cart, SPECIES_HTML_TARGET,speciesTarget); lowerString[0] = tolower(lowerString[0]); species = hashFindVal(orgHash, lowerString); if ((ptr = phyloFindPath(tree, database, species)) != NULL) speciesOrder = ptr; } #endif///ndef BRANEY_SAYS_USETARG_IS_OBSOLETE speciesCt = chopLine(cloneString(speciesOrder), species); for (i = 0; i < speciesCt; i++) { #ifndef BRANEY_SAYS_USETARG_IS_OBSOLETE if (!useTarg) #endif///ndef BRANEY_SAYS_USETARG_IS_OBSOLETE { /* skip this species if UI checkbox was unchecked */ if (!cartVarExistsAnyLevel(cart, track->tdb,FALSE,species[i])) { if (hashLookup(speciesOffHash, species[i])) { safef(option, sizeof(option), "%s.%s", prefix, species[i]); cartSetBoolean(cart, option, FALSE); } } if (!cartUsualBooleanClosestToHome(cart, track->tdb, FALSE, species[i],TRUE)) continue; } mi = newMafItem(species[i], group, lowerFirstChar); slAddHead(&miList, mi); } } slReverse(&miList); for (mi = miList; mi != NULL; mi = mi->next) { mi->height = height; dyStringPrintf(order, "%s ",mi->db); } safef(option, sizeof(option), "%s.speciesOrder", prefix); cartSetString(cart, option, order->string); slReverse(&miList); return miList; } static struct wigMafItem *scoreItem(int scoreHeight, char *label) /* Make up item that will show the score */ { struct wigMafItem *mi; AllocVar(mi); mi->name = cloneString(label); mi->height = scoreHeight; return mi; } struct mafPriv *getMafPriv(struct track *track) { struct mafPriv *mp = track->customPt; if (mp == NULL) { AllocVar(mp); track->customPt = mp; } return mp; } char *getTrackMafFile(struct track *track) /* look up MAF file name in track setting, return NULL if not found */ { return hashFindVal(track->tdb->settingsHash, "mafFile"); } char *getCustomMafFile(struct track *track) { char *fileName = getTrackMafFile(track); if (fileName == NULL) errAbort("cannot find custom maf setting"); return fileName; } static void loadMafsToTrack(struct track *track) /* load mafs in region to track custom pointer */ { struct sqlConnection *conn; struct sqlConnection *conn2; struct mafPriv *mp = getMafPriv(track); if (winBaseCount > MAF_SUMMARY_VIEW) return; /* we open two connections to the database * that has the maf track in it. One is * for the scoredRefs, the other to access * the extFile database. We could get away * with just one connection, but then we'd * have to allocate more memory to hold * the scoredRefs (whereas now we just use * one statically loaded scoredRef). */ if (mp->ct) { char *fileName = getCustomMafFile(track); conn = hAllocConn(CUSTOM_TRASH); conn2 = hAllocConn(CUSTOM_TRASH); mp->list = wigMafLoadInRegion(conn, conn2, mp->ct->dbTableName, chromName, winStart - 2 , winEnd + 2, fileName); hFreeConn(&conn); hFreeConn(&conn2); } else { char *fileName = getTrackMafFile(track); // optional conn = hAllocConn(database); conn2 = hAllocConn(database); mp->list = wigMafLoadInRegion(conn, conn2, track->table, chromName, winStart - 2 , winEnd + 2, fileName); hFreeConn(&conn); hFreeConn(&conn2); } } static struct wigMafItem *loadBaseByBaseItems(struct track *track) /* Make up base-by-base track items. */ { struct wigMafItem *miList = NULL, *speciesList = NULL, *mi; int scoreHeight = 0; bool lowerFirstChar = TRUE; char *firstCase; firstCase = trackDbSetting(track->tdb, ITEM_FIRST_CHAR_CASE); if (firstCase != NULL) { if (sameWord(firstCase, "noChange")) lowerFirstChar = FALSE; } loadMafsToTrack(track); /* NOTE: we are building up the item list backwards */ /* Add items for conservation wiggles */ struct track *wigTrack = track->subtracks; if (wigTrack) { enum trackVisibility wigVis = (wigTrack->limitedVis == tvDense ? tvDense : tvFull); while (wigTrack != NULL) { scoreHeight = wigTotalHeight(wigTrack, wigVis); mi = scoreItem(scoreHeight, wigTrack->shortLabel); slAddHead(&miList, mi); wigTrack = wigTrack->next; } } /* Make up item that will show gaps in this organism. */ AllocVar(mi); mi->name = GAP_ITEM_LABEL; mi->height = tl.fontHeight; slAddHead(&miList, mi); /* Make up item for this organism. */ mi = newMafItem(database, 0, lowerFirstChar); slAddHead(&miList, mi); speciesList = newSpeciesItems(track, tl.fontHeight); /* Make items for other species */ miList = slCat(speciesList, miList); slReverse(&miList); return miList; } static char *summarySetting(struct track *track) /* Return the setting for the MAF summary table * or NULL if none set */ { return trackDbSetting(track->tdb, SUMMARY_VAR); } static char *pairwiseSuffix(struct track *track) /* Return the suffix for the wiggle tables for the pairwise alignments, * or NULL if none set */ { char *suffix = trackDbSetting(track->tdb, PAIRWISE_VAR); if (suffix != NULL) suffix = firstWordInLine(cloneString(suffix)); return suffix; } static int pairwiseWigHeight(struct track *track) /* Return the height of a pairwise wiggle for this track, or 0 if n/a * NOTE: set one pixel smaller than we actually want, to * leave a border at the bottom of the wiggle. */ { char *words[2]; int wordCount; char *settings; struct track *wigTrack = track->subtracks; int pairwiseHeight = tl.fontHeight; int consWigHeight = 0; settings = cloneString(trackDbSetting(track->tdb, PAIRWISE_HEIGHT)); if (settings != NULL) return(atoi(firstWordInLine(settings))); if (wigTrack) { consWigHeight = wigTotalHeight(wigTrack, tvFull); pairwiseHeight = max(consWigHeight/3 - 1, pairwiseHeight); } settings = cloneString(trackDbSetting(track->tdb, PAIRWISE_VAR)); if (settings == NULL) return pairwiseHeight; /* get height for pairwise wiggles */ if ((wordCount = chopLine(settings, words)) > 1) { int settingsHeight = atoi(words[1]); if (settingsHeight < tl.fontHeight) pairwiseHeight = tl.fontHeight; else if (settingsHeight > consWigHeight && consWigHeight > 0) pairwiseHeight = consWigHeight; else pairwiseHeight = settingsHeight; } freez(&settings); return pairwiseHeight; } static char *getWigTablename(char *species, char *suffix) /* generate tablename for wiggle pairwise: "__wig" */ { char table[64]; safef(table, sizeof(table), "%s_%s_wig", species, suffix); return cloneString(table); } static char *getMafTablename(char *species, char *suffix) /* generate tablename for wiggle maf: "_
" */ { char table[64]; safef(table, sizeof(table), "%s_%s", species, suffix); return cloneString(table); } static boolean displayPairwise(struct track *track) /* determine if tables are present for pairwise display */ { return winBaseCount < MAF_SUMMARY_VIEW || isCustomTrack(track->table) || pairwiseSuffix(track) || summarySetting(track); } static boolean displayZoomedIn(struct track *track) /* determine if mafs are loaded -- zoomed in display */ { struct mafPriv *mp = getMafPriv(track); return mp->list != (char *)-1; } static void markNotPairwiseItem(struct wigMafItem *mi) { mi->ix = -1; } static boolean isPairwiseItem(struct wigMafItem *mi) { return mi->ix != -1; } static struct wigMafItem *loadPairwiseItems(struct track *track) /* Make up items for modes where pairwise data are shown. First an item for the score wiggle, then a pairwise item for each "other species" in the multiple alignment. These may be density plots (pack) or wiggles (full). Return item list. Also set customPt with mafList if zoomed in */ { struct wigMafItem *miList = NULL, *speciesItems = NULL, *mi; struct track *wigTrack = track->subtracks; int scoreHeight = tl.fontHeight * 4; if (winBaseCount < MAF_SUMMARY_VIEW) { /* "close in" display uses actual alignments from file */ struct mafPriv *mp = getMafPriv(track); struct sqlConnection *conn, *conn2; if (mp->ct) { char *fileName = getCustomMafFile(track); conn = hAllocConn(CUSTOM_TRASH); conn2 = hAllocConn(CUSTOM_TRASH); mp->list = wigMafLoadInRegion(conn, conn2, mp->ct->dbTableName, chromName, winStart, winEnd, fileName); hFreeConn(&conn); hFreeConn(&conn2); } else { char *fileName = getTrackMafFile(track); // optional conn = hAllocConn(database); conn2 = hAllocConn(database); mp->list = wigMafLoadInRegion(conn, conn2, track->table, chromName, winStart, winEnd, fileName); hFreeConn(&conn); hFreeConn(&conn2); } #ifdef DEBUG slSort(&mp->list, mafCmp); #endif } while (wigTrack != NULL) { /* display score graph along with pairs only if a wiggle * is provided */ scoreHeight = wigTotalHeight(wigTrack, tvFull); mi = scoreItem(scoreHeight, wigTrack->shortLabel); /* mark this as not a pairwise item */ markNotPairwiseItem(mi); slAddHead(&miList, mi); wigTrack = wigTrack->next; } if (displayPairwise(track)) /* make up items for other organisms by scanning through * all mafs and looking at database prefix to source. */ { speciesItems = newSpeciesItems(track, track->limitedVis == tvFull ? pairwiseWigHeight(track) : tl.fontHeight); miList = slCat(speciesItems, miList); } slReverse(&miList); return miList; } static struct wigMafItem *loadWigMafItems(struct track *track, boolean isBaseLevel) /* Load up items */ { struct wigMafItem *miList = NULL, *mi; int scoreHeight = tl.fontHeight * 4; struct track *wigTrack = track->subtracks; struct mafPriv *mp = getMafPriv(track); mp->list = (char *)-1; /* no maf's loaded or attempted to load */ /* if we're out in summary view and rendering a custom * track we force dense mode since we don't have * a summary table (yet). */ if ((winBaseCount >= MAF_SUMMARY_VIEW) && isCustomTrack(track->table)) track->limitedVis = tvDense; /* Load up mafs and store in track so drawer doesn't have * to do it again. */ /* Make up tracks for display. */ if (track->limitedVis == tvFull || track->limitedVis == tvPack) { if (isBaseLevel) { miList = loadBaseByBaseItems(track); } /* zoomed out */ else { miList = loadPairwiseItems(track); } } else if (track->limitedVis == tvSquish) { if (!wigTrack) { scoreHeight = tl.fontHeight * 4; if (winBaseCount < MAF_SUMMARY_VIEW) loadMafsToTrack(track); /* not a real meausre of conservation, so don't label it so */ miList = scoreItem(scoreHeight, ""); } else while (wigTrack) { /* have a wiggle */ scoreHeight = wigTotalHeight(wigTrack, tvFull); mi = scoreItem(scoreHeight, wigTrack->shortLabel); slAddTail(&miList, mi); wigTrack = wigTrack->next; } } else { /* dense mode, zoomed out - show density plot, with track label */ if (!wigTrack) { /* no wiggle -- use mafs if close in */ if (winBaseCount < MAF_SUMMARY_VIEW) #ifndef NOTYET loadMafsToTrack(track); AllocVar(miList); miList->name = cloneString(track->shortLabel); miList->height = tl.fontHeight; #else miList = loadPairwiseItems(track); /* remove components that aren't selected */ struct wigMafItem *mil = miList; struct hash *nameHash = newHash(5); for(;mil; mil = mil->next) hashStore(nameHash, mil->name); struct mafPriv *mp = getMafPriv(track); struct mafAli *mafList = mp->list; struct mafComp *mc; for(; mafList; mafList = mafList->next) { struct mafComp *prev = mafList->components; /* start after the master component */ for(mc = mafList->components->next; mc; mc = mc->next) { char *ptr = strchr(mc->src, '.'); if (ptr) *ptr = 0; char *ptr2 = hOrganism(mc->src); *ptr2 = tolower(*ptr2); if (!hashLookup(nameHash, ptr2)) /* delete this component */ prev->next = mc->next; else { if (ptr) *ptr = '.'; prev = mc; } } } /* label with the track name */ miList->name = cloneString(track->shortLabel); miList->next = NULL; #endif /* NOTYET */ } else while (wigTrack) { AllocVar(mi); mi->name = cloneString(wigTrack->shortLabel); mi->height = tl.fontHeight + 1; slAddTail(&miList, mi); wigTrack = wigTrack->next; } } return miList; } static void wigMafLoad(struct track *track) /* Load up maf tracks. What this will do depends on * the zoom level and the display density. */ { struct wigMafItem *miList = NULL; struct track *wigTrack = track->subtracks; // Make sure visibility takes into account any composite track with multi-views track->limitedVis = tvMin(track->visibility,limitedVisFromComposite(track)); track->limitedVisSet = (track->limitedVis != tvHide && track->visibility != track->limitedVis); miList = loadWigMafItems(track, zoomedToBaseLevel); track->items = miList; while (wigTrack != NULL) // load wiggle subtrack items { /* update track visibility from parent track, * since hgTracks will update parent vis before loadItems */ wigTrack->visibility = track->visibility; wigTrack->limitedVis = track->limitedVis; wigTrack->loadItems(wigTrack); wigTrack = wigTrack->next; } } static int wigMafTotalHeight(struct track *track, enum trackVisibility vis) /* Return total height of maf track. */ { struct wigMafItem *mi; int total = 0,count=0; for (mi = track->items; mi != NULL; mi = mi->next) { total += mi->height; count++; } track->height = total; if (track->limitedVis == tvDense) track->height=(tl.fontHeight+1)*count; // Evidence that track->height is 9 but should be 10! return track->height; } static int wigMafItemHeight(struct track *track, void *item) /* Return total height of maf track. */ { struct wigMafItem *mi = item; return mi->height; } static void wigMafFree(struct track *track) /* Free up maf items. */ { struct mafPriv *mp = getMafPriv(track); if (mp->list != NULL && mp->list != (char *)-1) mafAliFreeList((struct mafAli **)&mp->list); if (track->items != NULL) wigMafItemFreeList((struct wigMafItem **)&track->items); } static char *wigMafItemName(struct track *track, void *item) /* Return name of maf level track. */ { struct wigMafItem *mi = item; return mi->name; } static void processInserts(char *text, struct mafAli *maf, struct hash *itemHash, int insertCounts[], int baseCount) /* Make up insert line from sequence of reference species. It has a gap count at each displayed base position, and is generated by counting up '-' chars in the sequence, where */ { int i, baseIx = 0; struct mafComp *mc; char c; for (i=0; i < maf->textSize && baseIx < baseCount - 1; i++) { c = text[i]; if (c == '-') { for (mc = maf->components; mc != NULL; mc = mc->next) { char buf[64]; mafSrcDb(mc->src, buf, sizeof buf); if (hashLookup(itemHash, buf) == NULL) continue; if (mc->text == NULL) /* empty row annotation */ continue; if (mc->text[i] != '-') { insertCounts[baseIx]++; break; } } } else baseIx++; } } static int processSeq(char *text, char *masterText, int textSize, char *outLine, int offset, int outSize, int *inserts, int insertCounts) /* Add text to outLine, suppressing copy where there are dashes * in masterText. This effectively projects the alignment onto * the master genome. * If no dash exists in this sequence, count up size * of the insert and save in the line. */ { int i, outIx = 0, outPositions = 0; int insertSize = 0, previousInserts = 0; int previousBreaks = 0; outLine = outLine + offset + previousBreaks + (previousInserts * 2); for (i=0; i < textSize /*&& outPositions < outSize*/; i++) { if (masterText[i] != '-') { if (insertSize != 0) { inserts[insertCounts++] = offset + outIx + 1; insertSize = 0; } outLine[outIx++] = text[i]; outPositions++; } else { /* gap in master (reference) sequence but not in this species */ switch(text[i]) { case 'N': case '-': case '=': case ' ': break; default: insertSize++; } } } if (insertSize != 0) { inserts[insertCounts++] = offset + outIx + 1; } return insertCounts; } static int drawScore(float score, int chromStart, int chromEnd, int seqStart, double scale, struct hvGfx *hvg, int xOff, int yOff, int height, Color color, enum trackVisibility vis) /* Draw density plot or graph based on score. Return last X drawn */ { int x1,x2,y,w; int height1 = height-1; x1 = round((chromStart - seqStart)*scale); x2 = round((chromEnd - seqStart)*scale); w = x2-x1+1; if (vis == tvFull) { y = score * height1; hvGfxBox(hvg, x1 + xOff, yOff + height1 - y, w, y+1, color); } else { Color c; int shade = (score * maxShade) + 1; if (shade < 0) shade = 0; else if (shade > maxShade) shade = maxShade; c = shadesOfGray[shade]; hvGfxBox(hvg, x1 + xOff, yOff, w, height1, c); } return xOff + x1 + w - 1; } static void drawScoreSummary(struct mafSummary *summaryList, 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 chainBreaks) /* Draw density plot or graph for summary maf scores */ { struct mafSummary *ms; double scale = scaleForPixels(width); boolean isDouble = FALSE; int x1, x2; int w = 0; int chromStart = seqStart; int lastX; /* draw chain before first alignment */ ms = summaryList; if (chainBreaks && ms->chromStart > chromStart) { if (isContigOrTandem(ms->leftStatus[0]) || ms->leftStatus[0] == MAF_INSERT_STATUS) { isDouble = (ms->leftStatus[0] == MAF_INSERT_STATUS); x1 = xOff; x2 = round((double)((int)ms->chromStart-1 - seqStart) * scale) + xOff; w = x2 - x1; if (w > 0) drawMafChain(hvg, x1, yOff, w, height, isDouble); } else if (ms->leftStatus[0] == MAF_MISSING_STATUS ) { Color fuzz = shadesOfGray[1]; x1 = xOff; x2 = round((double)((int)ms->chromStart-1 - seqStart) * scale) + xOff; w = x2 - x1; hvGfxBox(hvg, x1, yOff, w, height, fuzz); } } for (ms = summaryList; ms != NULL; ms = ms->next) { lastX = drawScore(ms->score, ms->chromStart, ms->chromEnd, seqStart, scale, hvg, xOff, yOff, height, color, vis); /* draw chain after alignment */ if (chainBreaks && ms->chromEnd < seqEnd && ms->next != NULL) { if (isContigOrTandem(ms->rightStatus[0]) || ms->rightStatus[0] == MAF_INSERT_STATUS) { isDouble = (ms->rightStatus[0] == MAF_INSERT_STATUS); x1 = round((double)((int)ms->chromEnd+1 - seqStart) * scale) + xOff; x2 = round((double)((int)ms->next->chromStart-1 - seqStart) * scale) + xOff; w = x2 - x1; if (w == 1 && x1 == lastX) continue; if (w > 0); drawMafChain(hvg, x1, yOff, w, height, isDouble); } else if (ms->rightStatus[0] == MAF_MISSING_STATUS ) { Color fuzz = shadesOfGray[2]; x1 = round((double)((int)ms->chromEnd+1 - seqStart) * scale) + xOff; x2 = round((double)((int)ms->next->chromStart-1 - seqStart) * scale) + xOff ; w = x2 - x1; if (w == 1 && x1 == lastX) continue; if (w > 0) hvGfxBox(hvg, x1, yOff, w, height, fuzz); } } } } static void drawScoreOverviewC(struct sqlConnection *conn, char *tableName, int height, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, Color altColor, enum trackVisibility vis) /* Draw density plot or graph for overall maf scores rather than computing * by sections, for speed. Don't actually load the mafs -- just * the scored refs from the table. * TODO: reuse code in mafTrack.c */ { char **row; int rowOffset; struct sqlResult *sr = hRangeQuery(conn, tableName, chromName, seqStart, seqEnd, NULL, &rowOffset); double scale = scaleForPixels(width); while ((row = sqlNextRow(sr)) != NULL) { struct scoredRef ref; scoredRefStaticLoad(row + rowOffset, &ref); drawScore(ref.score, ref.chromStart, ref.chromEnd, seqStart, scale, hvg, xOff, yOff, height, color, vis); } sqlFreeResult(&sr); } static void drawScoreOverview(char *tableName, int height, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, Color altColor, enum trackVisibility vis) { struct sqlConnection *conn = hAllocConn(database); drawScoreOverviewC(conn, tableName, height, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, altColor, vis); hFreeConn(&conn); } static void drawScoreOverviewCT(char *tableName, int height, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, Color altColor, enum trackVisibility vis) { struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH); drawScoreOverviewC(conn, tableName, height, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, altColor, vis); hFreeConn(&conn); } static boolean drawPairsFromSummary(struct track *track, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) { /* Draw pairwise display for this multiple alignment */ char *summary; struct wigMafItem *miList = track->items, *mi = miList; struct sqlConnection *conn; struct sqlResult *sr = NULL; char **row = NULL; int rowOffset = 0; struct mafSummary *ms, *summaryList; struct hash *componentHash = newHash(6); struct hashEl *hel; struct hashCookie cookie; struct dyString *where = dyStringNew(256); char *whereClause = NULL; boolean useIrowChains = TRUE; char option[64]; if (miList == NULL) return FALSE; char *prefix = track->track; // use when setting things to the cart if (tdbIsContainerChild(track->tdb)) prefix = tdbGetContainer(track->tdb)->track; /* get summary table name from trackDb */ if ((summary = summarySetting(track)) == NULL) return FALSE; if (cartVarExistsAnyLevel(cart, track->tdb,FALSE,MAF_CHAIN_VAR)) useIrowChains = cartUsualBooleanClosestToHome(cart, track->tdb, FALSE, MAF_CHAIN_VAR,TRUE); else { char *irowString = trackDbSetting(track->tdb, "irows"); if (irowString && sameString(irowString, "off")) useIrowChains = FALSE; safef(option, sizeof(option), "%s.%s", prefix, MAF_CHAIN_VAR); cartSetBoolean(cart, option, useIrowChains); } /* Create SQL where clause that will load up just the * summaries for the species that we are including. */ conn = hAllocConn(database); dyStringAppend(where, "src in ("); for (mi = miList; mi != NULL; mi = mi->next) { if (!isPairwiseItem(mi)) /* exclude non-species items (e.g. conservation wiggle */ continue; dyStringPrintf(where, "'%s'", mi->db); if (mi->next != NULL) dyStringAppend(where, ","); } dyStringAppend(where, ")"); /* check for empty where clause */ if (!sameString(where->string,"src in ()")) whereClause = where->string; sr = hOrderedRangeQuery(conn, summary, chromName, seqStart, seqEnd, whereClause, &rowOffset); boolean hasFieldLeftStatus = hHasField(database, summary, "leftStatus"); /* Loop through result creating a hash of lists of maf summary blocks. * The hash is keyed by species. */ while ((row = sqlNextRow(sr)) != NULL) { if (hasFieldLeftStatus) ms = mafSummaryLoad(row + rowOffset); else /* previous table schema didn't have status fields */ ms = mafSummaryMiniLoad(row + rowOffset); /* prune to fit in window bounds */ if (ms->chromStart < seqStart) ms->chromStart = seqStart; if (ms->chromEnd > seqEnd) ms->chromEnd = seqEnd; if ((hel = hashLookup(componentHash, ms->src)) == NULL) hashAdd(componentHash, ms->src, ms); else slAddHead(&(hel->val), ms); } sqlFreeResult(&sr); /* reverse summary lists */ cookie = hashFirst(componentHash); while ((hel = hashNext(&cookie)) != NULL) slReverse(&hel->val); hFreeConn(&conn); /* display pairwise items */ for (mi = miList; mi != NULL; mi = mi->next) { if (mi->ix < 0) /* ignore item for the score */ continue; summaryList = (struct mafSummary *)hashFindVal(componentHash, mi->db); if (summaryList == NULL) summaryList = (struct mafSummary *)hashFindVal(componentHash, mi->name); if (summaryList != NULL) { if (vis == tvFull) { hvGfxSetClip(hvg, xOff, yOff, width, 16); drawScoreSummary(summaryList, mi->height, seqStart, seqEnd, hvg, xOff, yOff, width, font, track->ixAltColor, track->ixAltColor, tvFull, useIrowChains); hvGfxUnclip(hvg); } else { /* pack */ /* get maf table, containing pairwise alignments for this organism */ /* display pairwise alignments in this region in dense format */ hvGfxSetClip(hvg, xOff, yOff, width, mi->height); drawScoreSummary(summaryList, mi->height, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, color, tvDense, useIrowChains); hvGfxUnclip(hvg); } } yOff += mi->height; } return TRUE; } static boolean drawPairsFromPairwiseMafScores(struct track *track, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw pairwise display for this multiple alignment */ { char *suffix; char *tableName; Color pairColor; struct track *wigTrack = track->subtracks; int pairwiseHeight = pairwiseWigHeight(track); struct wigMafItem *miList = track->items, *mi = miList; if (miList == NULL) return FALSE; /* get pairwise table suffix from trackDb */ suffix = pairwiseSuffix(track); if (vis == tvFull && !isCustomTrack(track->table)) { double minY = 50.0; double maxY = 100.0; /* NOTE: later, remove requirement for wiggle */ if (wigTrack == NULL) return FALSE; /* swap colors for pairwise wiggles */ pairColor = wigTrack->ixColor; wigTrack->ixColor = wigTrack->ixAltColor; wigTrack->ixAltColor = pairColor; wigSetCart(wigTrack, MIN_Y, (void *)&minY); wigSetCart(wigTrack, MAX_Y, (void *)&maxY); } /* display pairwise items */ for (mi = miList; mi != NULL; mi = mi->next) { if (mi->ix < 0) /* ignore item for the score */ continue; if (isCustomTrack(track->table)) { struct mafPriv *mp = getMafPriv(track); drawScoreOverviewCT(mp->ct->dbTableName, mi->height, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, color, vis); hvGfxUnclip(hvg); } else if (vis == tvFull) { /* get wiggle table, of pairwise for example, percent identity */ tableName = getWigTablename(mi->name, suffix); if (!hTableExists(database, tableName)) tableName = getWigTablename(mi->db, suffix); if (hTableExists(database, tableName)) { /* reuse the wigTrack for pairwise tables */ wigTrack->track = tableName; wigTrack->table = tableName; wigTrack->loadItems(wigTrack); wigTrack->height = wigTrack->lineHeight = wigTrack->heightPer = pairwiseHeight - 1; /* clip, but leave 1 pixel border */ hvGfxSetClip(hvg, xOff, yOff, width, wigTrack->height); wigTrack->drawItems(wigTrack, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, tvFull); hvGfxUnclip(hvg); } else { /* no wiggle table for this -- compute a graph on-the-fly from mafs */ hvGfxSetClip(hvg, xOff, yOff, width, mi->height); tableName = getMafTablename(mi->name, suffix); if (!hTableExists(database, tableName)) tableName = getMafTablename(mi->db, suffix); if (hTableExists(database, tableName)) drawScoreOverview(tableName, mi->height, seqStart, seqEnd, hvg, xOff, yOff, width, font, track->ixAltColor, track->ixAltColor, tvFull); hvGfxUnclip(hvg); } /* need to add extra space between wiggles (for now) */ mi->height = pairwiseHeight; } else { /* pack */ /* get maf table, containing pairwise alignments for this organism */ /* display pairwise alignments in this region in dense format */ hvGfxSetClip(hvg, xOff, yOff, width, mi->height); tableName = getMafTablename(mi->name, suffix); if (!hTableExists(database, tableName)) tableName = getMafTablename(mi->db, suffix); if (hTableExists(database, tableName)) drawScoreOverview(tableName, mi->height, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, color, tvDense); hvGfxUnclip(hvg); } yOff += mi->height; } return TRUE; } static boolean drawPairsFromMultipleMaf(struct track *track, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw pairwise display from maf of multiple alignment. * Extract pairwise alignments from maf and rescore. * This is used only when zoomed-in. */ { struct wigMafItem *miList = track->items, *mi = miList; int graphHeight = 0; Color pairColor = (vis == tvFull ? track->ixAltColor : color); boolean useIrowChains = TRUE; char option[64]; char *prefix = track->track; // use when setting things to the cart if (tdbIsContainerChild(track->tdb)) prefix = track->tdb->parent->track; struct mafPriv *mp = getMafPriv(track); if (miList == NULL || mp->list == NULL) return FALSE; if (cartVarExistsAnyLevel(cart, track->tdb,FALSE,MAF_CHAIN_VAR)) useIrowChains = cartUsualBooleanClosestToHome(cart, track->tdb, FALSE, MAF_CHAIN_VAR,TRUE); else { char *irowString = trackDbSetting(track->tdb, "irows"); if (irowString && sameString(irowString, "off")) useIrowChains = FALSE; safef(option, sizeof(option), "%s.%s", prefix, MAF_CHAIN_VAR); cartSetBoolean(cart, option, useIrowChains); } if (vis == tvFull) graphHeight = pairwiseWigHeight(track); /* display pairwise items */ for (mi = miList; mi != NULL; mi = mi->next) { struct mafAli *mafList = NULL, *maf, *pairMaf; struct mafComp *mcThis, *mcPair = NULL, *mcMaster = NULL; if (mi->ix < 0) /* ignore item for the score */ continue; /* using maf sequences from file */ /* create pairwise maf list from the multiple maf */ struct mafPriv *mp = getMafPriv(track); for (maf = mp->list; maf != NULL; maf = maf->next) { if ((mcThis = mafMayFindCompSpecies(maf, mi->db, '.')) == NULL) continue; //if (mcPair->srcSize != 0) // TODO: replace with a cloneMafComp() AllocVar(mcPair); mcPair->src = cloneString(mcThis->src); mcPair->srcSize = mcThis->srcSize; mcPair->strand = mcThis->strand; mcPair->start = mcThis->start; mcPair->size = mcThis->size; mcPair->text = cloneString(mcThis->text); mcPair->leftStatus = mcThis->leftStatus; mcPair->leftLen = mcThis->leftLen; mcPair->rightStatus = mcThis->rightStatus; mcPair->rightLen = mcThis->rightLen; mcThis = mafFindCompSpecies(maf, database, '.'); AllocVar(mcMaster); mcMaster->src = cloneString(mcThis->src); mcMaster->srcSize = mcThis->srcSize; mcMaster->strand = mcThis->strand; mcMaster->start = mcThis->start; mcMaster->size = mcThis->size; mcMaster->text = cloneString(mcThis->text); mcMaster->next = mcPair; AllocVar(pairMaf); pairMaf->components = mcMaster; pairMaf->textSize = maf->textSize; slAddHead(&mafList, pairMaf); } slReverse(&mafList); /* compute a graph or density on-the-fly from mafs */ hvGfxSetClip(hvg, xOff, yOff, width, mi->height); drawMafRegionDetails(mafList, mi->height, seqStart, seqEnd, hvg, xOff, yOff, width, font, pairColor, pairColor, vis, FALSE, useIrowChains); hvGfxUnclip(hvg); /* need to add extra space between graphs ?? (for now) */ if (vis == tvFull) mi->height = graphHeight; yOff += mi->height; mafAliFreeList(&mafList); } return TRUE; } static boolean wigMafDrawPairwise(struct track *track, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw pairwise display for this multiple alignment * When zoomed in, use on-the-fly scoring of alignments extracted from multiple * When zoomed out: * if "pairwise" setting is on, use pairwise tables (maf or wiggle) * _ for maf, __wig for wiggle * For full mode, display graph. * for pack mode, display density plot. * if "summary" setting is on, use maf summary table * (saves space, and performs better) */ { if (displayZoomedIn(track)) return drawPairsFromMultipleMaf(track, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, vis); if (isCustomTrack(track->table) || pairwiseSuffix(track)) return drawPairsFromPairwiseMafScores(track, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, vis); if (summarySetting(track)) return drawPairsFromSummary(track, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, vis); return FALSE; } static void alternateBlocksBehindChars(struct hvGfx *hvg, int x, int y, int width, int height, int charWidth, int charCount, int stripeCharWidth, Color a, Color b) /* Draw blocks that alternate between color a and b. */ { int x1,x2 = x + width; int color = a; int i; for (i=0; itext && (!(ISGAPSPACEORN(comp->text[0]) ||ISGAPSPACEORN(ptr[0]) ||ISGAPSPACEORN(ptr[1])))) { if (strand == '-') complement(comp->text, 1); codon[0] = comp->text[0]; codon[1] = ptr[0]; codon[2] = ptr[1]; fillBox = TRUE; mult = 2; *ptr++ = ' '; *ptr++ = lookupAndCheckCodon(codon); } else ptr+=2; length -= 2; break; case 2: if (strand == '-') { ali = mafLoadInRegion2(conn, conn2, tableName, chromName, prevEnd, prevEnd + 2, mafFile); if (ali != NULL) sub = mafSubset(ali, masterChrom, prevEnd, prevEnd + 2 ); } else { ali = mafLoadInRegion2(conn, conn2, tableName, chromName, prevEnd - 1, prevEnd + 1, mafFile); if (ali != NULL) sub = mafSubset(ali, masterChrom, prevEnd - 1, prevEnd + 1); } if (sub != NULL) comp = mafMayFindCompSpecies(sub, compName, '.'); if (comp && comp->text && (!(ISGAPSPACEORN(comp->text[0])||ISGAPSPACEORN(comp->text[1]) ||ISGAPSPACEORN(*ptr)))) { if (strand == '-') reverseComplement(comp->text, 2); codon[0] = comp->text[0]; codon[1] = comp->text[1]; codon[2] = *ptr; fillBox = TRUE; mult = 1; *ptr++ = lookupAndCheckCodon(codon); } else ptr++; length -= 1; break; } } if (fillBox) { if (strand == '-') { x1 = x + ( end - ( 5 - frame) ) * width / winBaseCount; } else { x1 = x + (start - 2) * width / winBaseCount; } hvGfxBox(hvg, x1, y, mult*width/winBaseCount + 1 , height, color); } for (;length > 2; ptr +=3 , length -=3) { if (!(ISGAPSPACEORN(ptr[0]) || ISGAPSPACEORN(ptr[1]) || ISGAPSPACEORN(ptr[2]) )) { ptr[1] = lookupAndCheckCodon(ptr); ptr[0] = ' '; ptr[2] = ' '; if (strand == '-') { x1 = x + ( start + length - 3 - 2) * width / winBaseCount; } else { x1 = x + (end - length - 2) * width / winBaseCount; } if (color == shadesOfSea[0]) color = shadesOfSea[1]; else color = shadesOfSea[0]; hvGfxBox(hvg, x1, y, 3*width/winBaseCount + 1 , height, color); } } if (length && (nextStart != -1)) { char codon[4]; int mult = 1; boolean fillBox = FALSE; memset(codon, 0, sizeof(codon)); sub = NULL; if (strand == '-') { ali = mafLoadInRegion2(conn, conn2, tableName, chromName, nextStart - 2 + length, nextStart + 1, mafFile ); if (ali != NULL) sub = mafSubset(ali, masterChrom, nextStart - 2 + length, nextStart + 1); } else { ali = mafLoadInRegion2(conn, conn2, tableName, chromName, nextStart , nextStart + 2, mafFile ); if (ali != NULL) sub = mafSubset(ali, masterChrom, nextStart , nextStart + 2); } if (sub != NULL) comp = mafMayFindCompSpecies(sub, compName, '.'); if (sub && comp && comp->text) { switch(length) { case 2: if (strand == '-') complement(comp->text, 1); codon[0] = *ptr; codon[1] = *(1 + ptr); codon[2] = *comp->text; if (!(ISGAPSPACEORN(codon[0]) ||ISGAPSPACEORN(codon[1]) ||ISGAPSPACEORN(codon[2]))) { fillBox = TRUE; *ptr++ = ' '; *ptr++ = lookupAndCheckCodon(codon); mult = 2; } break; case 1: if (strand == '-') reverseComplement(comp->text, 2); codon[0] = *ptr; codon[1] = comp->text[0]; codon[2] = comp->text[1]; if (!(ISGAPSPACEORN(codon[0]) ||ISGAPSPACEORN(codon[1]) ||ISGAPSPACEORN(codon[2]))) { *ptr++ = lookupAndCheckCodon(codon); fillBox = TRUE; } break; } if (fillBox) { if (strand == '-') { x1 = x + ( start - 2 ) * width / winBaseCount; } else { x1 = x + (end - length - 2) * width / winBaseCount; } if (color == shadesOfSea[0]) color = shadesOfSea[1]; else color = shadesOfSea[0]; hvGfxBox(hvg, x1, y, mult*width/winBaseCount + 1 , height, color); } } } else if (length) /* broken frame, just show nucs */ { if (strand == '-') { if (!alreadyComplemented) complement(ptr, length); } else { if (alreadyComplemented) complement(ptr, length); } } if (strand == '-') reverseBytes(dna, size); } static int wigMafDrawBases(struct track *track, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis, struct wigMafItem *miList) /* Draw base-by-base view, return new Y offset. */ { struct wigMafItem *mi; struct mafAli *mafList, *maf, *sub; struct mafComp *mc, *mcMaster; int lineCount = slCount(miList); char **lines = NULL, *selfLine, *insertLine; int *insertCounts; int i, x = xOff, y = yOff; struct dnaSeq *seq = NULL; struct hash *miHash = newHash(9); struct hash *srcHash = newHash(0); char dbChrom[64]; char option[64]; int alignLineLength = winBaseCount * 2; /* doubled to allow space for insert counts */ boolean complementBases = cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE); bool dots = FALSE; /* configuration option */ /* this line must be longer than the longest base-level display */ char noAlignment[2000]; boolean useIrowChains = TRUE; int offset; char *framesTable = NULL; char *defaultCodonSpecies = cartUsualString(cart, SPECIES_CODON_DEFAULT, NULL); char *codonTransMode = NULL; boolean startSub2 = FALSE; int mafOrig = 0; int mafOrigOffset = 0; char query[256]; struct mafPriv *mp = getMafPriv(track); char *mafFile = NULL; struct sqlConnection *conn2 = NULL; struct sqlConnection *conn3 = NULL; char *tableName = NULL; char *prefix = track->track; // use when setting things to the cart if (tdbIsContainerChild(track->tdb)) prefix = tdbGetContainer(track->tdb)->track; if (mp->ct != NULL) { conn2 = hAllocConn(CUSTOM_TRASH); conn3 = hAllocConn(CUSTOM_TRASH); tableName = mp->ct->dbTableName; mafFile = getCustomMafFile(track); } else { conn2 = hAllocConn(database); conn3 = hAllocConn(database); tableName = track->table; mafFile = getTrackMafFile(track); // optional } if (hIsGsidServer()) { /* decide the value of mafOrigOffset to be used to display xxAaMaf tracks. */ struct sqlConnection *conn = hAllocConn(database); safef(query, sizeof(query), "select chromStart from %s", track->table); mafOrig = atoi(sqlNeedQuickString(conn, query)); mafOrigOffset = (mafOrig % 3) - 1; /* offset has to be non-negative */ if (mafOrigOffset < 0) mafOrigOffset = mafOrigOffset +3; hFreeConn(&conn); } if (defaultCodonSpecies == NULL) defaultCodonSpecies = trackDbSetting(track->tdb, "speciesCodonDefault"); if (defaultCodonSpecies == NULL) defaultCodonSpecies = database; if (seqStart > 2) { startSub2 = TRUE; seqStart -=2; } seqEnd +=2; if (seqEnd > seqBaseCount) seqEnd = seqBaseCount; if (cartVarExistsAnyLevel(cart, track->tdb,FALSE,MAF_DOT_VAR)) dots = cartUsualBooleanClosestToHome(cart, track->tdb, FALSE, MAF_DOT_VAR,TRUE); else { char *dotString = trackDbSetting(track->tdb, MAF_DOT_VAR); if (dotString && sameString(dotString, "on")) { dots = TRUE; safef(option, sizeof(option), "%s.%s", prefix, MAF_DOT_VAR); cartSetBoolean(cart, option, TRUE); } } if (cartVarExistsAnyLevel(cart, track->tdb,FALSE,"frames")) framesTable = cartOptionalStringClosestToHome(cart, track->tdb,FALSE,"frames"); else framesTable = trackDbSetting(track->tdb, "frames"); if (framesTable) { codonTransMode = cartUsualStringClosestToHome(cart, track->tdb,FALSE,"codons", "codonDefault"); if (sameString("codonNone", codonTransMode)) framesTable = NULL; } boolean newTableType = FALSE; if (framesTable != NULL) newTableType = hHasField(database, framesTable, "isExonStart"); /* initialize "no alignment" string to o's */ for (i = 0; i < sizeof noAlignment - 1; i++) noAlignment[i] = UNALIGNED_SEQ; if (cartVarExistsAnyLevel(cart, track->tdb,FALSE,MAF_CHAIN_VAR)) useIrowChains = cartUsualBooleanClosestToHome(cart, track->tdb, FALSE, MAF_CHAIN_VAR,TRUE); else { char *irowString = trackDbSetting(track->tdb, "irows"); if (irowString && sameString(irowString, "off")) useIrowChains = FALSE; safef(option, sizeof(option), "%s.%s", prefix, MAF_CHAIN_VAR); cartSetBoolean(cart, option, useIrowChains); } /* Allocate a line of characters for each item. */ AllocArray(lines, lineCount); lines[0] = needMem(alignLineLength); for (i=1; idna, winBaseCount + 4); //toUpperN(selfLine, winBaseCount); freeDnaSeq(&seq); /* Make hash of species 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. */ mafList = mp->list; safef(dbChrom, sizeof(dbChrom), "%s.%s", database, chromName); for (maf = mafList; maf != NULL; maf = maf->next) { int mafStart; /* get info about sequences from full alignment, for use later, when determining if sequence is unaligned or missing */ for (mc = maf->components; mc != NULL; mc = mc->next) if (!hashFindVal(srcHash, mc->src)) hashAdd(srcHash, mc->src, maf); mcMaster = mafFindComponent(maf, dbChrom); mafStart = mcMaster->start; /* get portion of maf in this window */ if (startSub2) sub = mafSubset(maf, dbChrom, winStart - 2, winEnd + 2); else sub = mafSubset(maf, dbChrom, winStart , winEnd + 2); if (sub != NULL) { int subStart,subEnd; int lineOffset, subSize; int startInserts = 0; char *ptr; /* process alignment for reference ("master") species */ for(ptr = mcMaster->text; *ptr == '-'; ptr++) startInserts++; mcMaster = mafFindComponent(sub, dbChrom); if (mcMaster->strand == '-') mafFlipStrand(sub); subStart = mcMaster->start; subEnd = subStart + mcMaster->size; subSize = subEnd - subStart; lineOffset = subStart - seqStart; processInserts(mcMaster->text, sub, miHash, &insertCounts[lineOffset], subSize); insertCounts[lineOffset] = max(insertCounts[lineOffset],startInserts); /* fill in bases for each species */ for (mi = miList; mi != NULL; mi = mi->next) { char *seq; bool needToFree = FALSE; int size = sub->textSize; if (mi->ix == 1) /* reference */ continue; if (mi->db == NULL) /* not a species line -- it's the gaps line, or... */ continue; if ((mc = mafMayFindCompSpecies(sub, mi->db, '.')) == NULL) continue; if (mafStart == subStart) { if (mc->size && mc->leftStatus == MAF_INSERT_STATUS && (*mc->text != '-') && !((lineOffset) && (((lines[mi->ix][lineOffset-1]) == '=') || (lines[mi->ix][lineOffset-1]) == '-'))) { insertCounts[lineOffset] = max(insertCounts[lineOffset],mc->leftLen); mi->inserts[mi->insertsSize] = (subStart - seqStart)+ 1; mi->insertsSize++; } } if (startInserts) { struct mafComp *mc1; if (((mc1 = mafMayFindCompSpecies(maf, mi->db, '.')) != NULL) && ((mc1->text) && (countNonDash(mc1->text, startInserts) > 0))) { mi->inserts[mi->insertsSize] = (subStart - seqStart)+ 1; mi->insertsSize++; } } seq = mc->text; if ( mc->text == NULL ) { /* if no alignment here, but MAF annotation indicates continuity * of flanking alignments, fill with dashes or ='s */ if (!useIrowChains) continue; if (isContigOrTandem(mc->leftStatus) && isContigOrTandem(mc->rightStatus)) { char fill = '-'; seq = needMem(size+1); needToFree = TRUE; memset(seq, fill, size); } else if (mc->leftStatus == MAF_INSERT_STATUS && mc->rightStatus == MAF_INSERT_STATUS) { char fill = MAF_DOUBLE_GAP; seq = needMem(size+1); needToFree = TRUE; memset(seq, fill, size); } else if (mc->leftStatus == MAF_MISSING_STATUS && mc->rightStatus == MAF_MISSING_STATUS) { char fill = 'N'; seq = needMem(size+1); needToFree = TRUE; memset(seq, fill, size); } else continue; } if (((mc->leftStatus == MAF_NEW_STATUS || mc->rightStatus == MAF_NEW_STATUS ) || (mc->leftStatus == MAF_NEW_NESTED_STATUS || mc->rightStatus == MAF_NEW_NESTED_STATUS ))) { int i; char *p; seq = needMem(size+1); needToFree = TRUE; for (p = seq, i = 0; i < size; p++, i++) *p = ' '; p = seq; if (mc->text != NULL) strcpy(p, mc->text); if (mc->leftStatus == MAF_NEW_STATUS) { char *m = mcMaster->text; if (mafStart == subStart) mi->seqEnds[mi->seqEndsSize] = (subStart - seqStart) + 1; while(*p == '-') { if ((*m++ != '-') && (mafStart == subStart)) mi->seqEnds[mi->seqEndsSize]++; *p++ = ' '; } if (mafStart == subStart) mi->seqEndsSize++; } if (mc->leftStatus == MAF_NEW_NESTED_STATUS) { char *m = mcMaster->text; if (mafStart == subStart) mi->brackStarts[mi->brackStartsSize] = (subStart - seqStart)+ 1; while(*p == '-') { *p++ = '='; if ((*m++ != '-') && (mafStart == subStart)) mi->brackStarts[mi->brackStartsSize]++; } if (mafStart == subStart) mi->brackStartsSize++; } if (mc->rightStatus == MAF_NEW_NESTED_STATUS) { char *p = seq + size - 1; char *m = mcMaster->text + size - 1; if (subEnd <= seqEnd) mi->brackEnds[mi->brackEndsSize] = (subStart - seqStart)+ subSize + 1; while(*p == '-') { if ((*m-- != '-') && (subEnd <= seqEnd)) mi->brackEnds[mi->brackEndsSize]--; *p-- = '='; } if (subEnd <= seqEnd) mi->brackEndsSize++; } if (mc->rightStatus == MAF_NEW_STATUS) { char *p = seq + size - 1; char *m = mcMaster->text + size - 1; if (mc->size && ((subEnd <= seqEnd))) mi->seqEnds[mi->seqEndsSize] = (subStart - seqStart)+ subSize + 1; while(*p == '-') { if ((*m-- != '-') && (mc->size && (subEnd <= seqEnd))) mi->seqEnds[mi->seqEndsSize]--; *p-- = ' '; } if (mc->size && ((subEnd <= seqEnd))) mi->seqEndsSize++; } } if (mc->text && ((mc->leftStatus == MAF_MISSING_STATUS))) { char *p = seq; while(*p == '-') *p++ = 'N'; } if (mc->text && ((mc->leftStatus == MAF_INSERT_STATUS))) { char *p = seq; while(*p == '-') *p++ = '='; } if (mc->text && ((mc->rightStatus == MAF_MISSING_STATUS))) { char *p = seq + size - 1; while(*p == '-') *p-- = 'N'; } if (mc->text && ((mc->rightStatus == MAF_INSERT_STATUS))) { char *m = mcMaster->text + size - 1; char *p = seq + size - 1; while((*p == '-') || (*m == '-')) { *p-- = '='; m--; } } mi->insertsSize = processSeq(seq, mcMaster->text, size, lines[mi->ix], lineOffset, subSize, mi->inserts, mi->insertsSize); if (needToFree) freeMem(seq); } } mafAliFree(&sub); } /* draw inserts line */ mi = miList; for(offset=startSub2*2; (offset < alignLineLength) && (offset < winBaseCount + startSub2 * 2); offset++) { int x1, x2; x1 = (offset - startSub2 * 2) * width/winBaseCount; x2 = ((offset - startSub2 * 2)+1) * width/winBaseCount - 1; if (insertCounts[offset] != 0) { struct dyString *label = newDyString(20); int haveRoomFor = (width/winBaseCount)/tl.mWidth; /* calculate number of AAs instead of bases if it is wigMafProt */ if (strstr(track->tdb->type, "wigMafProt")) { dyStringPrintf(label, "%d",insertCounts[offset]/3); } else { dyStringPrintf(label, "%d",insertCounts[offset]); } if (label->stringSize > haveRoomFor) { dyStringClear(label); dyStringPrintf(label, "%c",(insertCounts[offset] % 3) == 0 ? '*' : '+'); } hvGfxTextCentered(hvg, x1+x - (width/winBaseCount)/2, y, x2-x1, mi->height, getOrangeColor(), font, label->string); dyStringFree(&label); } } y += mi->height; /* draw alternating colors behind base-level alignments */ { int alternateColorBaseCount, alternateColorBaseOffset; alternateColorBaseCount = cartUsualIntClosestToHome(cart, track->tdb, FALSE, BASE_COLORS_VAR, 0); alternateColorBaseOffset = cartUsualIntClosestToHome(cart, track->tdb, FALSE, BASE_COLORS_OFFSET_VAR, 0); if (alternateColorBaseCount != 0) { int baseWidth = spreadStringCharWidth(width, winBaseCount); int colorX = x + alternateColorBaseOffset * baseWidth; alternateBlocksBehindChars(hvg, colorX, y-1, width, mi->height*(lineCount-1), tl.mWidth, winBaseCount, alternateColorBaseCount, shadesOfSea[0], MG_WHITE); } } /* draw base-level alignments */ for (mi = miList->next, i=1; mi != NULL && mi->db != NULL; mi = mi->next, i++) { char *line; line = lines[i]; /* TODO: leave lower case in to indicate masking ? * NOTE: want to make sure that all sequences are soft-masked * if we do this */ /* HAVE DONE: David doesn't like lower case by default * TODO: casing based on quality values ?? */ alignSeqToUpperN(line); if (complementBases) { complement(line, strlen(line)); } /* draw sequence letters for alignment */ hvGfxSetClip(hvg, x, y-1, width, mi->height); if (framesTable != NULL) { int rowOffset; char **row; struct sqlConnection *conn = hAllocConn(database); struct sqlResult *sr; char extra[512]; boolean found = FALSE; if (sameString("codonDefault", codonTransMode)) { safef(extra, sizeof(extra), "src='%s'",defaultCodonSpecies); found = TRUE; } else if (sameString("codonFrameDef", codonTransMode)) { safef(extra, sizeof(extra), "src='%s'",mi->db); found = FALSE; } else if (sameString("codonFrameNone", codonTransMode)) { safef(extra, sizeof(extra), "src='%s'",mi->db); found = TRUE; } else errAbort("unknown codon translation mode %s",codonTransMode); tryagain: sr = hRangeQuery(conn, framesTable, chromName, seqStart, seqEnd, extra, &rowOffset); while ((row = sqlNextRow(sr)) != NULL) { struct mafFrames mf; int start, end, w; int frame; found = TRUE; if (newTableType) mafFramesStaticLoad(row + rowOffset, &mf); else mafFramesStaticLoadOld(row + rowOffset, &mf); if (mf.chromStart < seqStart) start = 0; else start = mf.chromStart-seqStart; frame = mf.frame; if (mf.strand[0] == '-') { if (mf.chromEnd > seqEnd) frame = (frame + mf.chromEnd-seqEnd ) % 3; } else { if (mf.chromStart < seqStart) frame = (frame + seqStart-mf.chromStart ) % 3; } end = mf.chromEnd > seqEnd ? seqEnd - seqStart : mf.chromEnd - seqStart; w= end - start; translateCodons(conn2, conn3, tableName, mi->db, line, start, w, frame, mf.strand[0],mf.prevFramePos,mf.nextFramePos, complementBases, x, y, width, mi->height, hvg, mafFile); } sqlFreeResult(&sr); if (!found) { /* try the default species */ safef(extra, sizeof(extra), "src='%s'",defaultCodonSpecies); found = TRUE; /* don't try again */ goto tryagain; } hFreeConn(&conn); } if (startSub2) { if (strstr(track->tdb->type, "wigMafProt")) { spreadAlignStringProt(hvg, x, y, width, mi->height-1, color, font, &line[2], &selfLine[2], winBaseCount, dots, FALSE, seqStart, mafOrigOffset); } else { /* make sure we have bases to display before printing them */ if (strlen(line) > 2) spreadAlignString(hvg, x, y, width, mi->height-1, color, font, &line[2], &selfLine[2], winBaseCount, dots, FALSE); } } else spreadAlignString(hvg, x, y, width, mi->height-1, color, font, line, selfLine, winBaseCount, dots, FALSE); for(offset = 0; offset < mi->seqEndsSize; offset++) { int x1; x1 = x + (mi->seqEnds[offset] -1 - startSub2 * 2) * width/winBaseCount; hvGfxBox(hvg, x1, y-1, 1, mi->height-1, getChromBreakBlueColor()); } for(offset = 0; offset < mi->insertsSize; offset++) { int x1; x1 = x + (mi->inserts[offset] -1 - startSub2 * 2) * width/winBaseCount; hvGfxBox(hvg, x1, y-1, 1, mi->height-1, getOrangeColor()); } for(offset = 0; offset < mi->brackStartsSize; offset++) { int x1; x1 = x + (mi->brackStarts[offset] -1- startSub2 * 2) * width/winBaseCount; hvGfxBox(hvg, x1, y-1, 2, 1, getChromBreakGreenColor()); hvGfxBox(hvg, x1, y-1, 1, mi->height-1, getChromBreakGreenColor()); hvGfxBox(hvg, x1, y + mi->height-3, 2, 1, getChromBreakGreenColor()); } for(offset = 0; offset < mi->brackEndsSize; offset++) { int x1; x1 = x + (mi->brackEnds[offset] -1- startSub2 * 2) * width/winBaseCount; hvGfxBox(hvg, x1-1, y-1, 2, 1, getChromBreakGreenColor()); hvGfxBox(hvg, x1, y-1, 1, mi->height-1, getChromBreakGreenColor()); hvGfxBox(hvg, x1-1, y + mi->height-3, 2, 1, getChromBreakGreenColor()); } hvGfxUnclip(hvg); y += mi->height; } /* Clean up */ /* for (i=0; isubtracks; enum trackVisibility scoreVis; scoreVis = (vis == tvDense ? tvDense : tvFull); if (wigTrack == NULL) { /* no wiggle */ int height = tl.fontHeight * 4; if (vis == tvFull || vis == tvPack) /* suppress graph if other items displayed (bases or pairs) */ return yOff; else if (vis == tvDense) height = track->height; // Evidence that track-height comes in as 9 but should be 10! /* draw some kind of graph from multiple alignment */ struct mafPriv *mp = getMafPriv(track); if (mp->list != (char *)-1 && mp->list != NULL) { /* use mafs */ drawMafRegionDetails(mp->list, height, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, color, scoreVis, FALSE, FALSE); } else if (mp->ct != NULL) { /* use or scored refs from maf table*/ drawScoreOverviewCT(mp->ct->dbTableName, height, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, color, scoreVis); yOff++; } else { /* use or scored refs from maf table*/ drawScoreOverview(track->table, height, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, color, scoreVis); yOff++; } yOff += height; } else { Color wigColor = 0; while (wigTrack != NULL) { /* draw conservation wiggles */ if (!wigColor) wigColor = hvGfxFindRgb(hvg, &wigTrack->color); else wigColor = slightlyLighterColor(hvg, wigColor); wigTrack->ixColor = wigColor; wigTrack->ixAltColor = hvGfxFindRgb(hvg, &wigTrack->altColor); hvGfxSetClip(hvg, xOff, yOff, width, wigTotalHeight(wigTrack, scoreVis) - 1); wigTrack->drawItems(wigTrack, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, scoreVis); hvGfxUnclip(hvg); yOff += wigTotalHeight(wigTrack, scoreVis); wigTrack = wigTrack->next; } } return yOff; } static void wigMafDraw(struct track *track, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw routine for wigmaf type tracks */ { int y = yOff; y = wigMafDrawScoreGraph(track, seqStart, seqEnd, hvg, xOff, y, width, font, color, vis); if (vis == tvFull || vis == tvPack) { if (zoomedToBaseLevel) { struct wigMafItem *wiList = track->items; /* skip over cons wiggles */ struct track *wigTrack = track->subtracks; while (wigTrack) { wiList = wiList->next; wigTrack = wigTrack->next; } y = wigMafDrawBases(track, seqStart, seqEnd, hvg, xOff, y, width, font, color, vis, wiList); //MG_RED, vis, wiList); } else wigMafDrawPairwise(track, seqStart, seqEnd, hvg, xOff, y, width, font, color, vis); } mapBoxHc(hvg, seqStart, seqEnd, xOff, yOff, width, track->height, track->track, track->track, NULL); } void wigMafMethods(struct track *track, struct trackDb *tdb, int wordCount, char *words[]) /* Make track for maf multiple alignment. */ { struct track *wigTrack; int i; struct dyString *wigType; struct consWiggle *consWig, *consWigList = NULL; track->loadItems = wigMafLoad; track->freeItems = wigMafFree; track->drawItems = wigMafDraw; track->itemName = wigMafItemName; track->mapItemName = wigMafItemName; track->totalHeight = wigMafTotalHeight; track->itemHeight = wigMafItemHeight; track->itemStart = tgItemNoStart; track->itemEnd = tgItemNoEnd; track->itemLabelColor = wigMafItemLabelColor; track->mapsSelf = TRUE; //track->canPack = TRUE; /* deal with conservation wiggle(s) */ consWigList = wigMafWiggles(database, tdb); if (consWigList == NULL) return; /* determine which conservation wiggles to use -- from cart, or if none there, use first entry in trackDb setting */ boolean first = TRUE; for (consWig = consWigList; consWig != NULL; consWig = consWig->next) { if (differentString(consWig->leftLabel, DEFAULT_CONS_LABEL)) { char *wigVarSuffix = NULL; (void)wigMafWiggleVar(tdb->track, consWig,&wigVarSuffix); if (!cartUsualBooleanClosestToHome(cart, tdb, FALSE, wigVarSuffix, first)) continue; } first = FALSE; // Manufacture and initialize wiggle subtrack, both tdb and track struct trackDb *wigTdb = CloneVar(tdb); wigType = newDyString(64); dyStringPrintf(wigType, "type wig "); for (i = 1; i < wordCount; i++) dyStringPrintf(wigType, "%s ", words[i]); wigTdb->type = cloneString(wigType->string); wigTdb->track = consWig->table; wigTdb->table= consWig->table; /* Tweak wiggle left labels: replace underscore with space and * append 'Cons' */ struct dyString *ds = dyStringNew(0); dyStringAppend(ds, consWig->leftLabel); if (differentString(consWig->leftLabel, DEFAULT_CONS_LABEL)) dyStringAppend(ds, " Cons"); wigTdb->shortLabel = dyStringCannibalize(&ds); subChar(wigTdb->shortLabel, '_', ' '); wigTrack = trackFromTrackDb(wigTdb); /* setup wiggle methods in subtrack */ wigMethods(wigTrack, tdb, wordCount, words); wigTrack->mapsSelf = FALSE; wigTrack->drawLeftLabels = NULL; wigTrack->next = NULL; slAddTail(&track->subtracks, wigTrack); dyStringFree(&wigType); } }