ee190bc09015bf1d10254691824b0a38f59e1aff
braney
Fri Sep 4 11:39:56 2026 -0700
hgc: show the lifted alignment on a quickLifted psl or bigPsl details page
The details page and its alignment views all read the assembly on screen, so a
quickLifted alignment track either reported the position it had before the lift
or, for the base alignment views, could not find its item at all.
Finding the track was the first problem. aliTable is the table name from the
assembly the alignments came from, and that name usually belongs to a real table
on the assembly being viewed as well, so it cannot tell the two apart. The
alignment links now carry aliTrack, the track hgc was called on, built the same
way hgcAnchorSomewhere builds its table parameter. A hub track's trackDb only
reaches trackHash if its hub is attached in cartDoMiddle, which was already being
done for the two htcBigPsl commands and now covers the cdna and protein ones too.
quickLiftAliInfo resolves the trackDb, assembly, table and chain file behind an
aliTable and aliTrack pair, custom tracks included. quickLiftFindPsl finds the
alignment a link points at: only the reference position is known there and the
lift does not run backwards, so it reads every alignment of the accession out of
the other assembly, lifts them, and keeps the one that lands on that position.
htcCdnaAli and htcCdnaAliInWindow use that, and read the CDS and the query
sequence from the source assembly. htcBigPslAli and htcBigPslAliInWindow read
the source intervals through quickLiftGetIntervals and match on the lifted
position rather than on the raw interval. Several name tests that decide what
kind of alignment this is now skip the hub prefix, so the guard against a
translated alignment in a window is not bypassed on a lifted xeno track.
Separately, and not specific to quickLift: genericBigPslClick read through an
empty alignment list, which is what happens whenever nothing in the window
matches the item asked for.
refs #38249
diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c
index 017daa1bb4e..3896466c7db 100644
--- src/hg/hgc/hgc.c
+++ src/hg/hgc/hgc.c
@@ -3456,34 +3456,44 @@
if (start == end)
{
// item is an insertion; expand the search range from 0 bases to 2 so we catch it:
ivStart = max(0, start-1);
ivEnd++;
}
if (cfgOptionBooleanDefault("drawDot", FALSE))
bigPslDotPlot(tdb, bbi, seqName, winStart, winEnd);
boolean showEvery = sameString(item, "PrintAllSequences");
boolean showAll = trackDbSettingOn(tdb, "showAll");
unsigned seqTypeField = bbExtraFieldIndex(bbi, "seqType");
struct bigBedInterval *bb, *bbList = NULL;
+struct hash *chainHash = NULL;
+struct hash *mapPsls = NULL; // mapping alignments quickLift reuses across items
+char *quickLiftFile = trackDbSetting(tdb, "quickLiftUrl");
+// A quickLifted track can only show what the chains around this window reach, so it takes
+// the windowed query even when the track asks for every alignment of the item. The file
+// holds the other assembly's alignments, so the window has to be turned into that
+// assembly's coordinates before the query. quickLiftGetIntervals also hands back the
+// chains needed to bring the alignments the other way.
// If showAll is on, show all alignments with this qName, not just the
// selected one.
-if (showEvery)
+if (quickLiftFile != NULL)
+ bbList = quickLiftGetIntervals(quickLiftFile, bbi, seqName, ivStart, ivEnd, &chainHash);
+else if (showEvery)
{
struct bbiChromInfo *chrom, *chromList = bbiChromList(bbi);
for (chrom = chromList; chrom != NULL; chrom = chrom->next)
{
char *chromName = chrom->name;
int start = 0, end = chrom->size;
int itemsLeft = 0; // Zero actually means no limit....
struct bigBedInterval *intervalList = bigBedIntervalQuery(bbi, chromName,
start, end, itemsLeft, lm);
slCat(&bbList, intervalList);
}
}
else if (showAll)
{
int fieldIx;
@@ -3529,30 +3539,38 @@
boolean firstTime = TRUE;
struct hash *seqHash = hashNew(0);
struct dyString *sequencesText = dyStringNew(256);
int sequencesFound = 0;
for (bb = bbList; bb != NULL; bb = bb->next)
{
bbiCachedChromLookup(bbi, bb->chromId, lastChromId, chromName, sizeof(chromName));
lastChromId=bb->chromId;
bigBedIntervalToRow(bb, chromName, startBuf, endBuf, bedRow, 4);
if (showEvery || sameString(bedRow[3], item))
{
char *cdsStr, *seq;
struct psl *psl= getPslAndSeq(tdb, chromName, bb, seqTypeField, &seq, &cdsStr);
+ if (chainHash != NULL)
+ {
+ struct psl *lifted = quickLiftPsl(chainHash, &mapPsls, psl);
+ pslFree(&psl);
+ if (lifted == NULL)
+ continue; // nothing in the chains places this alignment
+ psl = lifted;
+ }
slAddHead(&pslList, psl);
// we're assuming that if there are multiple psl's with the same id that
// they are the same query sequence so we only put out one set of sequences
if (!hashLookup(seqHash, bedRow[3]) && !isEmpty(seq)) // if there is a query sequence
{
if (firstTime)
{
firstTime = FALSE;
printf("
Links to sequence:
\n");
printf("\n");
}
if (!isEmpty(cdsStr)) // if we have CDS
{
@@ -3588,62 +3606,77 @@
if (!firstTime)
printf("
\n");
freeHash(&seqHash);
char *sort = cartUsualString(cart, "sort", pslSortList[0]);
pslSortListByVar(&pslList, sort);
if (showEvery)
printf("Genomic Alignments
");
else
printf("%s/Genomic Alignments
", item);
/* Hub track names have special characters replaced with underbar, but tdb->table does not. */
char *aliTable = cloneString(tdb->table);
if (isHubTrack(aliTable))
trackHubFixName(aliTable);
-if (showEvery || pslIsProtein(pslList))
+// pslIsProtein reads through its argument, and the list is empty whenever nothing in the
+// window matched the item, or, on a quickLifted track, nothing in the window could be
+// lifted.
+if (showEvery || ((pslList != NULL) && pslIsProtein(pslList)))
printAlignmentsSimple(pslList, start, "htcBigPslAli", aliTable, item);
else
printAlignmentsExtra(pslList, start, "htcBigPslAli", "htcBigPslAliInWindow",
aliTable, item);
freeMem(aliTable);
pslFreeList(&pslList);
if (showEvery && sequencesFound > 0)
{
printf("
\n");
printf("Input Sequences:
\n");
printf("\n");
dyStringFree(&sequencesText);
}
printItemDetailsHtml(tdb, item);
}
void genericPslClick(struct sqlConnection *conn, struct trackDb *tdb,
char *item, int start, char *subType)
/* Handle click in generic psl track. */
{
struct psl* pslList = getAlignments(conn, tdb->table, item);
+// For a quickLifted track the alignments came out of the other assembly, and so did the
+// sequence the check below looks for, so both have to name that assembly. Move the
+// alignments onto the reference before anything prints a position. Only the ones the
+// chains around this window can place survive, which leaves out alignments of the same
+// accession elsewhere in the genome.
+char *liftDb = trackDbSetting(tdb, "quickLiftDb");
+char *srcDb = (liftDb != NULL) ? liftDb : database;
+char *quickLiftFile = trackDbSetting(tdb, "quickLiftUrl");
+if ((quickLiftFile != NULL) && (pslList != NULL))
+ pslList = quickLiftPsls(quickLiftChainHash(quickLiftFile, seqName, winStart, winEnd),
+ pslList);
+
/* check if there is an alignment available for this sequence. This checks
* both genbank sequences and other sequences in the seq table. If so,
* set it up so they can click through to the alignment. */
-if (hGenBankHaveSeq(database, item, NULL))
+if (hGenBankHaveSeq(srcDb, item, NULL))
{
printf("%s/Genomic Alignments
", item);
if (sameString("protein", subType))
printAlignments(pslList, start, "htcProteinAli", tdb->table, item);
else
printAlignments(pslList, start, "htcCdnaAli", tdb->table, item);
}
else
{
/* just dump the psls */
pslDumpHtml(pslList);
}
pslFreeList(&pslList);
printItemDetailsHtml(tdb, item);
}
@@ -6960,30 +6993,42 @@
if (end != 0 && differentString(chrom,"0") && isNotEmpty(chrom))
{
printf("Position: "
"",
hgTracksPathAndSettings(), database, chrom, start+1, end);
printf("%s:%d-%d
\n", chrom, start+1, end);
}
gbWarnFree(&gbWarn);
sqlFreeResult(&sr);
dyStringFree(&dy);
hFreeConn(&conn);
hFreeConn(&conn2);
}
+static char *aliTrackParam()
+/* "&aliTrack=