715e2acf2dd9ef6106cc42ce79c724e1e52ad62e
braney
Thu Mar 12 09:14:28 2026 -0700
NoDots MAF alignment display for hgc mafClick, with i-row preservation and mafFrag -noDots flag
hgMaf.c: add hgMafFragHelperNoDots and public wrappers (hgMafFragNoDots,
hgBigMafFragNoDots, hgMafFragFromMafListNoDots) that return a list of maf
blocks containing only species with actual sequence — no dot-filled rows.
Blocks are broken when the species set changes; gaps between same-species
blocks are filled with native sequence for the reference and dashes for
others. Preserve i-row data (leftStatus/rightStatus/leftLen/rightLen)
through the NoDots path so insert annotations appear in emitted blocks.
hgMaf.h: declare the new NoDots public functions.
mafClick.c: use NoDots path when mafClickMafFrag is enabled. Fix block
numbering (aliIx was never incremented in useMafFrag path). Use full
textSize for NoDots line width. Use dots instead of spaces in diff mode
for both paths. Fix species label width computation to check labelHash
consistently so long assembly names don't misalign sequences. Strip
ref gap columns where no other species has sequence.
mafFrag: add -noDots option to invoke hgMafFragNoDots from the command line,
with 4 new tests (noDots, noDotsRev, noDotsOutName, noDotsLarger).
refs #21477
Co-Authored-By: Claude Opus 4.6
diff --git src/hg/hgc/mafClick.c src/hg/hgc/mafClick.c
index 950171b68d2..183dc44fc03 100644
--- src/hg/hgc/mafClick.c
+++ src/hg/hgc/mafClick.c
@@ -1,1015 +1,1027 @@
/* Handle details pages for maf tracks and axt tracks. */
/* Copyright (C) 2013 The Regents of the University of California
* See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
#include "common.h"
#include "hash.h"
#include "linefile.h"
#include "hgc.h"
#include "maf.h"
#include "obscure.h"
#include "cheapcgi.h"
#include "genePred.h"
#include "botDelay.h"
#include "hgMaf.h"
#include "hui.h"
#include "hCommon.h"
#include "hubConnect.h"
#include "trackHub.h"
#include "chromAlias.h"
#include "hgConfig.h"
extern boolean issueBotWarning;
#define ADDEXONCAPITAL
/* Javascript to help make a selection from a drop-down
* go back to the server. */
static char *autoSubmit = "document.gpForm.submit();";
static void blueCapWrite(FILE *f, char *s, int size, char *r)
/* Write capital letters in blue. */
{
boolean isBlue = FALSE;
int i;
for (i=0; i");
isBlue = TRUE;
}
}
else if (islower(c))
{
if (isBlue)
{
fprintf(f, "");
isBlue = FALSE;
}
}
fprintf(f, "%c", c);
}
}
if (isBlue)
fprintf(f, "");
}
void initSummaryLine(char *summaryLine, int size, int val)
/* Fill summary line with stars and null terminate */
{
int i;
for (i = 0; i < size; i++)
summaryLine[i] = val;
summaryLine[i] = 0;
}
void updateSummaryLine(char *summaryLine, char *referenceText,
char *alignText, int size)
/* Blank out columns in the summary line where this alignment
* differs from the reference */
{
int i;
for (i=0; icomponents;
startChars = sizeChars = srcSizeChars = 0;
for (mc = maf->components; mc != NULL; mc = mc->next)
{
/* Figure out length of source (species) field. */
/*if (mc->size != 0)*/
{
char dbOnly[128];
int len;
char *org;
memset(dbOnly, 0, sizeof(dbOnly));
safef(dbOnly, sizeof(dbOnly), "%s", mc->src);
chopPrefix(dbOnly);
+ if ((labelHash == NULL) || ((org = hashFindVal(labelHash, dbOnly)) == NULL))
+ {
if ((org = hOrganism(dbOnly)) == NULL)
- len = strlen(dbOnly);
- else
+ org = dbOnly;
+ }
len = strlen(org);
if (srcChars < len)
srcChars = len;
len = digitsBaseTen(mc->start);
if (startChars < len)
startChars = len;
len = digitsBaseTen(mc->size);
if (sizeChars < len)
sizeChars = len;
len = digitsBaseTen(mc->srcSize);
if (srcSizeChars < len)
srcSizeChars = len;
if (mc->text && (mc->rightStatus == MAF_INSERT_STATUS) && (masterMc->start + masterMc->size < winEnd))
haveInserts = TRUE;
#ifdef REVERSESTRAND
/* complement bases if hgTracks is on reverse strand */
if (mc->size && cartCgiUsualBoolean(cart, COMPLEMENT_BASES_VAR, FALSE))
complement(mc->text, maf->textSize);
#endif
}
}
/* first sequence in the alignment */
referenceText = maf->components->text;
for (lineStart = 0; lineStart < maf->textSize; lineStart = lineEnd)
{
int size;
lineEnd = lineStart + lineSize;
if (lineEnd >= maf->textSize)
lineEnd = maf->textSize;
size = lineEnd - lineStart;
initSummaryLine(summaryLine, size, '*');
for (mc = maf->components; mc != NULL; mc = mc->next)
{
char dbOnly[128], *chrom;
int s = mc->start;
int e = s + mc->size;
char *org;
char *revComp = "";
char strand = mc->strand;
struct dyString *dy = dyStringNew(512);
#ifdef REVERSESTRAND
if (cartCgiUsualBoolean(cart, COMPLEMENT_BASES_VAR, FALSE))
strand = (strand == '+') ? '-' : '+';
#endif
if (strand == '-') revComp = "&hgSeq.revComp=on";
dyStringClear(dy);
safef(dbOnly, sizeof(dbOnly), "%s", mc->src);
chrom = chopPrefix(dbOnly);
if ((labelHash == NULL) || ((org = hashFindVal(labelHash, dbOnly)) == NULL))
{
if ((org = hOrganism(dbOnly)) == NULL)
org = dbOnly;
}
if (mc->strand == '-')
reverseIntRange(&s, &e, mc->srcSize);
if (mc->text != NULL)
{
if (lineStart == 0)
{
if (hDbIsActive(dbOnly))
{
dyStringPrintf(dy, "%s Browser %s:%d-%d %c %*dbps",hOrganism(dbOnly),chrom, s+1, e, mc->strand,sizeChars, mc->size);
linkToOtherBrowserTitle(dbOnly, chrom, s, e, dy->string);
dyStringClear(dy);
fprintf(f, "B ");
}
else
fprintf(f, " ");
if (hDbExists(dbOnly))
{
dyStringPrintf(dy, "Get %s DNA %s:%d-%d %c %*dbps",hOrganism(dbOnly),chrom, s+1, e, mc->strand,sizeChars, mc->size);
printf("D ", dy->string,hgcName(),
s, cgiEncode(chrom),
chrom, s, e, dbOnly, revComp);
}
else
fprintf(f, " ");
}
else
{
fprintf(f, " ");
}
dyStringClear(dy);
dyStringPrintf(dy, "%s:%d-%d %c %*dbps",chrom, s+1, e, mc->strand,sizeChars, mc->size);
fprintf(f, " %*s ", dy->string, srcChars, org);
updateSummaryLine(summaryLine, referenceText + lineStart, mc->text + lineStart, size);
blueCapWrite(f, mc->text + lineStart, size,
(onlyDiff && mc != maf->components) ? referenceText + lineStart : NULL);
fprintf(f, "\n");
}
else
{
if (((mc->leftStatus == MAF_CONTIG_STATUS) && (mc->rightStatus == MAF_CONTIG_STATUS) )
|| ((mc->leftStatus == MAF_TANDEM_STATUS) && (mc->rightStatus == MAF_TANDEM_STATUS) )
|| ((mc->leftStatus == MAF_INSERT_STATUS) && (mc->rightStatus == MAF_INSERT_STATUS) )
|| ((mc->leftStatus == MAF_MISSING_STATUS) && (mc->rightStatus == MAF_MISSING_STATUS) ))
{
if (lineStart == 0)
{
int s = mc->start;
int e = s + mc->rightLen;
struct dyString *dy = dyStringNew(512);
if (mc->strand == '-')
reverseIntRange(&s, &e, mc->srcSize);
if ( hDbIsActive(dbOnly))
{
dyStringPrintf(dy, "%s Browser %s:%d-%d %c %d bps Unaligned",hOrganism(dbOnly),chrom, s+1, e, mc->strand, e-s);
linkToOtherBrowserTitle(dbOnly, chrom, s, e, dy->string);
fprintf(f,"B ");
dyStringClear(dy);
}
else
fprintf(f," ");
if (hDbExists(dbOnly))
{
dyStringPrintf(dy, "Get %s DNA %s:%d-%d %c %d bps Unaligned",hOrganism(dbOnly),chrom, s+1, e, mc->strand, e-s);
printf("D ", dy->string, hgcName(),
s, cgiEncode(chrom),
chrom, s, e, dbOnly,revComp);
}
else
fprintf(f, " ");
}
else
fprintf(f, " ");
initSummaryLine(summaryLine, size, ' ');
dyStringClear(dy);
dyStringPrintf(dy, "%s:%d-%d %c %*dbps",chrom, s+1, e, mc->strand,sizeChars, mc->size);
fprintf(f, "%*s ", dy->string, srcChars, org);
ch = '-';
switch(mc->rightStatus)
{
case MAF_INSERT_STATUS:
ch = '=';
break;
case MAF_MISSING_STATUS:
ch = 'N';
break;
case MAF_TANDEM_STATUS:
case MAF_CONTIG_STATUS:
ch = '-';
break;
}
for(ii=lineStart; ii < lineEnd ; ii++)
fputc(ch,f);
fprintf(f,"\n");
}
}
}
#ifdef ADDMATCHLINE
if (lineStart == 0)
fprintf(f, " %-*s %s\n", srcChars, "", summaryLine);
else
fprintf(f, "%-*s %s\n", srcChars, "", summaryLine);
#else
fprintf(f, "\n");
#endif
}
if (haveInserts)
{
fprintf(f, "Inserts between block %d and %d in window\n",blockNo, blockNo+1);
for (mc = maf->components; mc != NULL; mc = mc->next)
{
char dbOnly[128], *chrom;
int s = mc->start + mc->size;
int e = s + mc->rightLen;
char *org;
if (mc->text == NULL)
continue;
if (mc->strand == '-')
reverseIntRange(&s, &e, mc->srcSize);
safef(dbOnly, sizeof(dbOnly), "%s", mc->src);
chrom = chopPrefix(dbOnly);
if ((labelHash == NULL) || ((org = hashFindVal(labelHash, dbOnly)) == NULL))
{
if ((org = hOrganism(dbOnly)) == NULL)
org = dbOnly;
}
if (mc->rightStatus == MAF_INSERT_STATUS)
{
char *revComp = "";
if (hDbIsActive(dbOnly))
{
char strand = mc->strand;
#ifdef REVERSESTRAND
if (cartCgiUsualBoolean(cart, COMPLEMENT_BASES_VAR, FALSE))
strand = (strand == '+') ? '-' : '+';
#endif
if (strand == '-') revComp = "&hgSeq.revComp=on";
linkToOtherBrowser(dbOnly, chrom, s, e);
fprintf(f,"B");
fprintf(f, "");
fprintf(f, " ");
}
else
fprintf(f, " ");
if (hDbExists(dbOnly))
{
printf("D ", hgcName(), s, cgiEncode(chrom), chrom, s,
e, dbOnly,revComp);
}
else
fprintf(f, " ");
fprintf(f, "%*s %dbp\n", srcChars, org,mc->rightLen);
}
}
fprintf(f, "\n");
}
freeMem(summaryLine);
}
static void mafLowerCase(struct mafAli *maf)
/* Lower case letters in maf. */
{
struct mafComp *mc;
for (mc = maf->components; mc != NULL; mc = mc->next)
if (mc->size != 0)
tolowers(mc->text);
}
#ifdef ADDEXONCAPITAL
static boolean findAliRange(char *ali, int aliSize, int start, int end,
int *retStart, int *retEnd)
/* Convert start/end in sequence coordinates to alignment
* coordinates (that include dashes). Return FALSE if
* no intersection. */
{
int i, baseIx=0;
char c;
int rStart = 0, rEnd = 0;
if (start >= end)
return FALSE;
for (i=0; i= rEnd)
return FALSE;
*retStart = rStart;
*retEnd = rEnd;
return TRUE;
}
static void capAliTextOnTrack(struct mafAli *maf,
char *db, char *chrom,
char *track, boolean onlyCds)
/* Capitalize exons in alignment. */
{
int rowOffset;
struct sqlConnection *conn = sqlConnect(db);
struct mafComp *selfMc = maf->components, *mc;
int start = selfMc->start;
int end = start + selfMc->size;
struct sqlResult *sr = hRangeQuery(conn, track, chrom, start, end,
NULL, &rowOffset);
char **row;
while ((row = sqlNextRow(sr)) != NULL)
{
struct genePred *gp = genePredLoad(row+rowOffset);
int i;
for (i=0; iexonCount; ++i)
{
int s = gp->exonStarts[i];
int e = gp->exonEnds[i];
if (onlyCds)
{
if (s < gp->cdsStart) s = gp->cdsStart;
if (e > gp->cdsEnd) e = gp->cdsEnd;
}
if (s < start) s = start;
if (e > end) e = end;
if (findAliRange(selfMc->text, maf->textSize, s-start, e-start, &s, &e))
{
for (mc = maf->components; mc != NULL; mc = mc->next)
if (mc->text)
toUpperN(mc->text + s, e-s);
}
}
genePredFree(&gp);
}
sqlFreeResult(&sr);
sqlDisconnect(&conn);
}
static void capMafOnTrack(struct mafAli *maf, char *track, boolean onlyCds)
/* Capitalize parts of maf that correspond to exons according
* to given gene prediction track. */
{
char dbOnly[64];
char *chrom;
struct mafComp *mc = maf->components;
strncpy(dbOnly, mc->src, sizeof(dbOnly));
chrom = chopPrefix(dbOnly);
capAliTextOnTrack(maf, dbOnly, chrom, track, onlyCds);
}
#endif
-static void mafStripRefGaps(struct mafAli *maf)
-/* Remove columns where the reference (first component) has a gap character.
- * These are insertions in non-reference species that should be collapsed
- * when displaying in reference coordinates. */
+static void mafStripEmptyRefGaps(struct mafAli *maf)
+/* Remove columns where the reference (first component) has a gap character
+ * AND no other component has actual sequence in that column. Columns where
+ * at least one non-reference species has a base are kept. */
{
struct mafComp *mc;
struct mafComp *ref = maf->components;
if (ref == NULL || ref->text == NULL)
return;
int textSize = maf->textSize;
-/* Build boolean array of columns to keep (where ref is not a gap) */
+/* Build boolean array of columns to keep */
bool *keep = needMem(textSize);
int newSize = 0;
int ii;
for (ii = 0; ii < textSize; ii++)
{
if (ref->text[ii] != '-')
{
keep[ii] = TRUE;
newSize++;
}
+ else
+ {
+ /* Reference has gap — check if any other species has sequence */
+ for (mc = ref->next; mc != NULL; mc = mc->next)
+ {
+ if (mc->text != NULL && isalpha(mc->text[ii]))
+ {
+ keep[ii] = TRUE;
+ newSize++;
+ break;
+ }
+ }
+ }
}
if (newSize == textSize)
{
freeMem(keep);
- return; /* nothing to strip */
+ return;
}
/* Compact all component texts in place */
for (mc = maf->components; mc != NULL; mc = mc->next)
{
if (mc->text == NULL)
continue;
int jj = 0;
for (ii = 0; ii < textSize; ii++)
if (keep[ii])
mc->text[jj++] = mc->text[ii];
mc->text[jj] = '\0';
}
maf->textSize = newSize;
freeMem(keep);
}
static struct mafAli *mafOrAxtLoadInRegion2(struct sqlConnection *conn,struct sqlConnection *conn2,
struct trackDb *tdb, char *chrom, int start, int end,
char *axtOtherDb, char *file)
{
if (axtOtherDb != NULL)
{
struct hash *qSizeHash = hChromSizeHash(axtOtherDb);
struct mafAli *mafList = axtLoadAsMafInRegion(conn, tdb->table,
chrom, start, end, database, axtOtherDb, hChromSize(database, chrom), qSizeHash);
hashFree(&qSizeHash);
return mafList;
}
else
return mafLoadInRegion2(conn, conn2, tdb->table, chrom,
start, end, file);
}
#ifdef ADDEXONCAPITAL
static char *codeAll[] = {
"coding",
"all",
};
#endif
static char *showAll[] = {
"all",
"diff",
};
static void conservationStatsLink(struct trackDb *tdb,
char *label, char *table)
/* write link that to display statistics of phastCons table */
{
char *chrom = cartCgiUsualString(cart, "c", "chr7");
printf("%s",
hgcPathAndSettings(), table, table, chrom,
winStart, winEnd, winStart, database, tdb->track, label);
}
static void mafOrAxtClick2(struct sqlConnection *conn, struct sqlConnection *conn2, struct trackDb *tdb, char *axtOtherDb, char *fileName)
/* Display details for MAF or AXT tracks. */
{
boolean useMafFrag = cfgOptionBooleanDefault("mafClickMafFrag", FALSE);
if (issueBotWarning)
{
char *ip = getenv("REMOTE_ADDR");
botDelayMessage(ip, botDelayMillis);
}
if (winEnd - winStart > 30000)
{
printf("Zoom so that window is 30,000 bases or less to see alignments and conservation statistics\n");
}
else
{
struct mafAli *mafList = NULL, *maf, *subList = NULL;
int aliIx = 0, realCount = 0;
char dbChrom[64];
char option[128];
char *capTrack;
struct consWiggle *consWig, *consWiggles;
struct hash *speciesOffHash = NULL;
char *speciesOrder = NULL;
char *speciesTarget = trackDbSetting(tdb, SPECIES_TARGET_VAR);
char buffer[1024];
int useTarg = FALSE;
int useIrowChains = FALSE;
struct hash *labelHash = mafGetLabelHash(tdb);
struct slName *orderList = NULL;
safef(option, sizeof(option), "%s.%s", tdb->track, MAF_CHAIN_VAR);
if (cartCgiUsualBoolean(cart, option, FALSE) &&
trackDbSetting(tdb, "irows") != NULL)
useIrowChains = TRUE;
safef(buffer, sizeof(buffer), "%s.vis",tdb->track);
if (useIrowChains)
{
if (!cartVarExists(cart, buffer) && (speciesTarget != NULL))
useTarg = TRUE;
else
{
char *val;
val = cartUsualString(cart, buffer, "useCheck");
useTarg = sameString("useTarg",val);
}
}
if (useMafFrag)
{
/* New mafFrag code path: determine species order from trackDb settings,
* matching hgTracks logic, then use hgMafFrag to stitch into a single
* continuous alignment in reference coordinates */
char *speciesGroup = trackDbSetting(tdb, SPECIES_GROUP_VAR);
char *speciesUseFile = trackDbSetting(tdb, SPECIES_USE_FILE);
speciesOrder = trackDbSetting(tdb, SPECIES_ORDER_VAR);
/* Check cart override for speciesOrder */
safef(option, sizeof(option), "%s.speciesOrder", tdb->track);
char *cartOrder = cartUsualString(cart, option, NULL);
if (cartOrder != NULL)
speciesOrder = cartOrder;
if (speciesUseFile)
speciesOrder = cartGetOrderFromFile(database, cart, speciesUseFile);
/* Build hash of species that default to off */
char *speciesOff = trackDbSetting(tdb, SPECIES_DEFAULT_OFF_VAR);
struct hash *defaultOffHash = NULL;
if (speciesOff)
{
char *offSpecies[2048];
int offCt = chopLine(cloneString(speciesOff), offSpecies);
defaultOffHash = newHash(5);
int ii;
for (ii = 0; ii < offCt; ii++)
hashAdd(defaultOffHash, offSpecies[ii], NULL);
}
/* Build orderList of selected species for mafFrag, matching
* newSpeciesItems() logic in wigMafTrack.c */
if (speciesOrder || speciesGroup)
{
char *groups[1000];
char sGroup[2048];
int groupCt = 1;
int group;
if (speciesGroup)
groupCt = chopLine(cloneString(speciesGroup), groups);
/* Add reference database as first in list */
slNameAddHead(&orderList, database);
for (group = 0; group < groupCt; group++)
{
char *species[2048];
int speciesCt;
if (groupCt != 1 || !speciesOrder)
{
safef(sGroup, sizeof sGroup, "%s%s",
SPECIES_GROUP_PREFIX, groups[group]);
speciesOrder = trackDbRequiredSetting(tdb, sGroup);
}
speciesCt = chopLine(cloneString(speciesOrder), species);
int ii;
for (ii = 0; ii < speciesCt; ii++)
{
boolean defaultOn = (defaultOffHash == NULL
|| hashLookup(defaultOffHash, species[ii]) == NULL);
if (useTarg || cartUsualBooleanClosestToHome(cart, tdb,
FALSE, species[ii], defaultOn))
{
slNameAddTail(&orderList, species[ii]);
}
else
{
if (speciesOffHash == NULL)
speciesOffHash = newHash(4);
char *organism = hOrganism(species[ii]);
if (!organism)
organism = species[ii];
hashStoreName(speciesOffHash, organism);
}
}
}
}
- /* Load stitched alignment using mafFrag approach */
+ /* Load alignment using mafFragNoDots approach — returns a list of
+ * maf blocks, each containing only assemblies with actual sequence. */
if (sameString(tdb->type, "bigMaf"))
{
char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl");
struct bbiFile *bbi = bigBedFileOpenAlias(bigDataUrl, chromAliasFindAliases);
- maf = hgBigMafFrag(database, bbi, seqName, winStart, winEnd, '+', NULL, orderList);
+ subList = hgBigMafFragNoDots(database, bbi, seqName, winStart, winEnd, '+', NULL, orderList);
bbiFileClose(&bbi);
}
else if (axtOtherDb == NULL && fileName == NULL)
{
/* Regular MAF from database */
- maf = hgMafFrag(database, tdb->table, seqName, winStart, winEnd, '+', NULL, orderList);
+ subList = hgMafFragNoDots(database, tdb->table, seqName, winStart, winEnd, '+', NULL, orderList);
}
else
{
/* AXT or MAF with external file - load blocks, then stitch */
mafList = mafOrAxtLoadInRegion2(conn, conn2, tdb, seqName,
winStart, winEnd, axtOtherDb, fileName);
- maf = hgMafFragFromMafList(database, seqName, winStart, winEnd, '+',
+ subList = hgMafFragFromMafListNoDots(database, seqName, winStart, winEnd, '+',
mafList, NULL, orderList);
- mafList = NULL; /* consumed by hgMafFragFromMafList */
+ mafList = NULL; /* consumed by hgMafFragFromMafListNoDots */
}
- /* Remove insertion columns (where reference has gaps) */
- if (maf != NULL)
- mafStripRefGaps(maf);
+ /* Strip ref gap columns where no other species has sequence */
+ for (maf = subList; maf != NULL; maf = maf->next)
+ mafStripEmptyRefGaps(maf);
- if (maf != NULL)
- slAddHead(&subList, maf);
- realCount = (subList != NULL) ? 1 : 0;
+ realCount = slCount(subList);
}
else
{
/* Original block-by-block code path */
if (sameString(tdb->type, "bigMaf"))
{
char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl");
struct bbiFile *bbi = bigBedFileOpenAlias(bigDataUrl, chromAliasFindAliases);
mafList = bigMafLoadInRegion(bbi, seqName, winStart, winEnd);
}
else
mafList = mafOrAxtLoadInRegion2(conn, conn2, tdb, seqName, winStart, winEnd,
axtOtherDb, fileName);
safef(dbChrom, sizeof(dbChrom), "%s.%s", hubConnectSkipHubPrefix(database), seqName);
safef(option, sizeof(option), "%s.speciesOrder", tdb->track);
speciesOrder = cartUsualString(cart, option, NULL);
if (speciesOrder == NULL)
speciesOrder = trackDbSetting(tdb, "speciesOrder");
int speciesCt = 0;
char *species[2048];
struct mafComp **newOrder;
if (speciesOrder)
{
speciesCt = chopLine(cloneString(speciesOrder), species);
newOrder = needMem((speciesCt + 1) * sizeof (struct mafComp *));
int ii;
struct hash *nameHash = newHash(5);
for(ii=0; ii < speciesCt; ii++)
{
if (hashLookup(nameHash, species[ii]))
errAbort("speciesOrder contains %s more than once.", species[ii]);
hashStore(nameHash, species[ii]);
}
}
for (maf = mafList; maf != NULL; maf = maf->next)
{
int mcCount = 0;
struct mafComp *mc;
struct mafAli *subset;
struct mafComp *nextMc;
if (!useTarg)
{
for (mc = maf->components->next; mc != NULL; mc = nextMc)
{
char buf[64];
char *organism;
mafSrcDb(mc->src, buf, sizeof buf);
organism = hOrganism(buf);
if (!organism)
organism = buf;
nextMc = mc->next;
safef(option, sizeof(option), "%s.%s", tdb->track, buf);
if (!cartUsualBoolean(cart, option, TRUE))
{
if (speciesOffHash == NULL)
speciesOffHash = newHash(4);
hashStoreName(speciesOffHash, organism);
}
if (!cartUsualBoolean(cart, option, TRUE))
slRemoveEl(&maf->components, mc);
else
mcCount++;
}
}
if (mcCount == 0)
continue;
if (speciesCt)
{
struct mafComp *mcThis;
int i;
mcCount = 0;
speciesCt = chopLine(cloneString(speciesOrder), species);
newOrder = needMem((speciesCt + 1) * sizeof (struct mafComp *));
newOrder[mcCount++] = maf->components;
for (i = 0; i < speciesCt; i++)
{
if ((mcThis = mafMayFindCompSpecies(maf, species[i], '.')) == NULL)
continue;
if (mcThis == maf->components)
errAbort("Reference species (%s) shouldn't be in speciesOrder in trackDb", species[i]);
newOrder[mcCount++] = mcThis;
}
maf->components = NULL;
for (i = 0; i < mcCount; i++)
{
newOrder[i]->next = 0;
slAddHead(&maf->components, newOrder[i]);
}
slReverse(&maf->components);
}
subset = mafSubsetE(maf, dbChrom, winStart, winEnd, TRUE);
if (subset != NULL)
{
mafMoveComponentToTop(subset, dbChrom);
if (subset->components->strand == '-')
mafFlipStrand(subset);
subset->score = mafScoreMultiz(subset);
slAddHead(&subList, subset);
++realCount;
}
}
slReverse(&subList);
mafAliFreeList(&mafList);
}
if (subList != NULL)
{
char *showVarName = "hgc.showMultiBase";
char *showVarVal = cartUsualString(cart, showVarName, "all");
boolean onlyDiff = sameWord(showVarVal, "diff");
#ifdef ADDEXONCAPITAL
char *codeVarName = "hgc.multiCapCoding";
char *codeVarVal = cartUsualString(cart, codeVarName, "coding");
boolean onlyCds = sameWord(codeVarVal, "coding");
#endif
/* add links for conservation score statistics */
consWiggles = wigMafWiggles(database, tdb);
int wigCount = slCount(consWiggles);
if (wigCount == 1)
{
conservationStatsLink(tdb, "Conservation score statistics", consWiggles->table);
}
else if (wigCount > 1)
{
/* multiple wiggles. List all that have been turned on with
* checkboxes */
/* Scan for cart variables -- do any exist, are any turned on ? */
boolean wigSet = FALSE;
boolean wigOn = FALSE;
for (consWig = consWiggles; consWig != NULL;
consWig = consWig->next)
{
char *wigVarSuffix = NULL;
(void)wigMafWiggleVar(tdb->track, consWig, &wigVarSuffix);
if (cartVarExistsAnyLevel(cart, tdb, FALSE, wigVarSuffix))
{
wigSet = TRUE;
if (cartBooleanClosestToHome(cart, tdb, FALSE, wigVarSuffix))
wigOn = TRUE;
}
}
/* If there are no cart vars, turn on the first (default) wig */
if (!wigSet)
{
char *prefix = tdb->track; // use when setting things to the cart
if (tdbIsContainerChild(tdb))
prefix = tdbGetContainer(tdb)->track;
cartSetBoolean(cart, wigMafWiggleVar(prefix, consWiggles, NULL), TRUE);
wigOn = TRUE;
}
if (wigOn)
{
boolean first = TRUE;
for (consWig = consWiggles; consWig != NULL;
consWig = consWig->next)
{
if (first)
{
printf("Conservation score statistics:");
first = FALSE;
}
char *wigVarSuffix = NULL;
(void)wigMafWiggleVar(tdb->track, consWig, &wigVarSuffix);
if (cartUsualBooleanClosestToHome(cart, tdb, FALSE, wigVarSuffix,FALSE))
{
printf(" ");
subChar(consWig->uiLabel, '_', ' ');
conservationStatsLink(tdb,
consWig->uiLabel, consWig->table);
}
}
}
}
puts("
\n");
/* no alignment to display when in visibilities where only wiggle is shown */
char *vis = cartOptionalString(cart, tdb->track);
if (vis)
{
enum trackVisibility tv = hTvFromStringNoAbort(vis);
if (tv == tvSquish || tv == tvDense)
return;
}
#ifdef ADDEXONCAPITAL
puts("\n");
#ifdef REVERSESTRAND
/* notify if bases are complemented (hgTracks is on reverse strand) */
if (cartCgiUsualBoolean(cart, COMPLEMENT_BASES_VAR, FALSE))
puts("Alignment displayed on reverse strand
");
#endif
puts("Place cursor over species for alignment detail. Click on 'B' to link to browser ");
puts("for aligned species, click on 'D' to get DNA for aligned species.
");
printf("");
/* notify if species removed from alignment */
if (speciesOffHash)
{
char *species;
struct hashCookie hc = hashFirst(speciesOffHash);
puts("Components not displayed: ");
while ((species = hashNextName(&hc)) != NULL)
printf("%s ", species);
puts("
");
}
for (maf = subList; maf != NULL; maf = maf->next)
{
mafLowerCase(maf);
#ifdef ADDEXONCAPITAL
if (capTrack != NULL)
capMafOnTrack(maf, capTrack, onlyCds);
#endif
+ ++aliIx;
if (useMafFrag)
printf("Alignment %d - %d, %d bps \n",
maf->components->start + 1,
maf->components->start + maf->components->size,
maf->components->size);
else
printf("Alignment block %d of %d in window, %d - %d, %d bps \n",
- ++aliIx,realCount,maf->components->start + 1,
+ aliIx,realCount,maf->components->start + 1,
maf->components->start + maf->components->size, maf->components->size);
- mafPrettyOut(stdout, maf, 70, onlyDiff, aliIx, labelHash);
+ mafPrettyOut(stdout, maf, useMafFrag ? maf->textSize : 70, onlyDiff, aliIx, labelHash);
}
mafAliFreeList(&subList);
}
else
{
printf("No multiple alignment in browser window");
}
printf("");
slNameFreeList(&orderList);
}
}
static void mafOrAxtClick(struct sqlConnection *conn, struct trackDb *tdb, char *axtOtherDb)
{
struct sqlConnection *conn2 = NULL;
if (!(isHubTrack(tdb->track) || trackHubDatabase(database)))
conn2 = hAllocConn(database);
// MAF file location is optionally in trackDb
char *mafFile = hashFindVal(tdb->settingsHash, "mafFile");
mafOrAxtClick2(conn, conn2, tdb, axtOtherDb, mafFile);
hFreeConn(&conn2);
}
void customMafClick(struct sqlConnection *conn, struct sqlConnection *conn2,
struct trackDb *tdb)
{
struct hash *settings = tdb->settingsHash;
char *fileName;
if ((fileName = hashFindVal(settings, "mafFile")) == NULL)
errAbort("cannot find custom maf file setting");
mafOrAxtClick2(conn, conn2, tdb, NULL, fileName);
}
void genericMafClick(struct sqlConnection *conn, struct trackDb *tdb,
char *item, int start)
/* Display details for MAF tracks. */
{
mafOrAxtClick(conn, tdb, NULL);
}
void genericAxtClick(struct sqlConnection *conn, struct trackDb *tdb,
char *item, int start, char *otherDb)
/* Display details for AXT tracks. */
{
mafOrAxtClick(conn, tdb, otherDb);
}