8fb78633bc427f68f8d2c6b1a75d8093883a0b80
braney
Fri Sep 4 12:48:23 2026 -0700
hgc: show the lifted chain on a quickLifted chain track's details page
chainLoadItemInRange is the one place all four chain detail consumers get their
chain, so lifting there covers the details page, the base by base alignment, its
translated variant, and quickLift's own difference page at once.
quickLiftChainInRange loads every chain over the source ranges and matches on id
rather than asking for the one id. The chain's sequence name in the other
assembly is not known at that point, and both by-id loaders abort when the id is
not inside the range they were handed.
The page used to end by saying the fields above describe the entire chain rather
than the part in the window. For a lifted chain that is not true, since only the
part around the window is ever worked out, so it now says that instead. Native
chain tracks keep the original sentence.
refs #38249
diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c
index 3896466c7db..092fd629965 100644
--- src/hg/hgc/hgc.c
+++ src/hg/hgc/hgc.c
@@ -132,30 +132,31 @@
#include "genomicSuperDups.h"
#include "celeraDupPositive.h"
#include "celeraCoverage.h"
#include "sample.h"
#include "axt.h"
#include "axtInfo.h"
#include "jaxQTL.h"
#include "jaxQTL3.h"
#include "wgRna.h"
#include "ncRna.h"
#include "gbProtAnn.h"
#include "hgSeq.h"
#include "chain.h"
#include "chainDb.h"
#include "chainNetDbLoad.h"
+#include "bigChain.h"
#include "chainToPsl.h"
#include "chainToAxt.h"
#include "netAlign.h"
#include "stsMapRat.h"
#include "stsInfoRat.h"
#include "stsMapMouseNew.h"
#include "stsInfoMouseNew.h"
#include "vegaInfo.h"
#include "vegaInfoZfish.h"
#include "ensInfo.h"
#include "scoredRef.h"
#include "blastTab.h"
#include "hdb.h"
#include "hgc.h"
#include "genbank.h"
@@ -3781,35 +3782,91 @@
{
htmlHorizontalLine();
// Add pennantIcon
printPennantIconNote(tdb);
// Wrap description html in div with limited width, so when the page is very wide
// due to long details, the user doesn't have to scroll right to read the description.
puts("
");
puts(html);
puts("
");
}
hPrintf("
\n");
}
+static struct chain *quickLiftChainInRange(struct trackDb *tdb, int id)
+/* Load one chain out of the assembly the track came from and map it onto the reference.
+ * Every chain in the window is loaded and then matched on id, rather than asking for the
+ * one id: the chain's sequence name in the other assembly is not known here, and the
+ * by-id loaders abort when the id is not in the range they were given. */
+{
+char *liftDb = trackDbSetting(tdb, "quickLiftDb");
+char *table = NULL;
+quickLiftResolveTable(tdb, trackHubSkipHubName(tdb->table), &table, &liftDb);
+char *quickLiftFile = trackDbSetting(tdb, "quickLiftUrl");
+
+char *chainFile = NULL, *linkFile = NULL;
+if (startsWith("big", tdb->type))
+ {
+ chainFile = trackDbSetting(tdb, "bigDataUrl");
+ linkFile = trackDbSetting(tdb, "linkDataUrl");
+ if (linkFile == NULL)
+ linkFile = bigChainGetLinkFile(chainFile);
+ }
+
+struct hash *chainHash = newHash(8);
+struct hash *mapPsls = NULL;
+struct quickLiftRange *range, *rangeList = quickLiftSourceRanges(quickLiftFile, seqName,
+ winStart, winEnd, chainHash);
+
+for (range = rangeList; range != NULL; range = range->next)
+ {
+ struct chain *chain, *chainList;
+ if (chainFile != NULL)
+ chainList = chainLoadIdRangeHub(NULL, chainFile, linkFile, range->chrom,
+ range->start, range->end, -1);
+ else
+ chainList = chainLoadRange(liftDb, table, range->chrom, range->start, range->end);
+
+ for (chain = chainList; chain != NULL; chain = chain->next)
+ {
+ if (chain->id != id)
+ continue;
+
+ struct chain *lifted = quickLiftChain(chainHash, &mapPsls, chain);
+ if (lifted != NULL)
+ return lifted;
+ }
+ }
+return NULL;
+}
+
struct chain *chainLoadItemInRange(struct trackDb *tdb, char *item)
/* Load up parts of chain that intersect seqName:winStart-winEnd */
{
struct chain *chain = NULL;
int id = sqlUnsigned(item);
+
+if ((trackDbSetting(tdb, "quickLiftDb") != NULL) && !quickLiftIsOwnChainTrack(tdb))
+ {
+ chain = quickLiftChainInRange(tdb, id);
+ if (chain == NULL)
+ errAbort("Couldn't lift chain %d into %s:%d-%d", id, seqName, winStart, winEnd);
+ return chain;
+ }
+
if (startsWith("big", tdb->type))
{
char *fileName = trackDbSetting(tdb, "bigDataUrl");
char *linkFileName = trackDbSetting(tdb, "linkDataUrl");
if (linkFileName == NULL)
{
char *bigDataUrl = cloneString(trackDbSetting(tdb, "bigDataUrl"));
char *dot = strrchr(bigDataUrl, '.');
if (dot == NULL)
errAbort("No linkDataUrl in track %s", tdb->track);
*dot = 0;
char buffer[4096];
safef(buffer, sizeof buffer, "%s.link.bb", bigDataUrl);
linkFileName = buffer;
}
@@ -4116,30 +4173,38 @@
struct sqlResult *sr;
char **row;
sqlSafef(query, ArraySize(query),
"select normScore from %s where id = '%s'", tableName, item);
sr = sqlGetResult(conn, query);
if ((row = sqlNextRow(sr)) != NULL)
{
double normScore = atof(row[0]);
int basesAligned = chain->score / normScore;
printf("Normalized Score: %1.0f (aligned bases: %d)", normScore, basesAligned);
}
sqlFreeResult(&sr);
printf("
\n");
}
+if ((trackDbSetting(tdb, "quickLiftDb") != NULL) && !quickLiftIsOwnChainTrack(tdb))
+ // A lifted chain is only worked out over the window being viewed, so the whole chain's
+ // extent is not knowable here and the usual sentence would be wrong.
+ printf("
This chain comes from %s and is mapped onto %s as the browser draws it, so "
+ "the fields above describe the part of it around the window rather than the "
+ "whole chain.
\n",
+ trackDbSetting(tdb, "quickLiftDb"), trackHubSkipHubName(database));
+else
printf("
Fields above refer to entire chain or gap, not just the part inside the window.
\n");
printf("
\n");
chainWinSize = min(winEnd-winStart, chain->tEnd - chain->tStart);
/* Show alignment if the database exists and */
/* if there is a chromInfo table for that database and the sequence */
/* file exists. This means that alignments can be shown on the archive */
/* server (or in other cases) if there is a database with a chromInfo table, */
/* the sequences are available and there is an entry added to dbDb for */
/* the otherDb. */
if (otherTbf != NULL ||
(!startsWith("big", tdb->type) && sqlDatabaseExists(otherDb)
&& chromSeqFileExists(otherDb, chain->qName)))
{
if (chainWinSize < 1000000)