f2e67daf98177a40eea3e1d52364b52c4abf4728
braney
  Fri Sep 4 13:08:29 2026 -0700
hgTracks: draw quickLifted bigMaf and wigMaf tracks

quickLiftLoadMafs is reached from both places a maf track loads its blocks.  It
reads them over the ranges in the other assembly through the existing readers and
maps them onto the reference.

A lifted maf cannot use its summary.  The summary names a table or a file in the
assembly the track came from, so it cannot be queried with reference coordinates,
and for a hub track the setting comes back rewritten as a path under the hub
besides, which is how it was failing:  a range query against
../trash/quickLift/NNN/multiz100waySummary.  inSummaryMode now says no for a
lifted track and the real blocks are read instead.

That costs time at a wide window, since reading the summary is exactly the work a
maf track avoids that way.  Measured on multiz100way, hg19 lifted onto hg38:  at
900 kb, where both read real blocks, native is 1.52s and lifted is 1.09s, so
nothing about the lift is slow.  At 2.5 Mb, where native reads the summary,
native is 1.05s and lifted is 7.0s.  Lifting the summary is the fix and wants its
own pass:  a summary row is a coordinate range and a score, so mapping one is
liftOverRemapRange, but it is read in the drawing functions rather than in a
loader.

refs #38249

diff --git src/hg/hgTracks/wigMafTrack.c src/hg/hgTracks/wigMafTrack.c
index 324e0acb7e0..1bcec853473 100644
--- src/hg/hgTracks/wigMafTrack.c
+++ src/hg/hgTracks/wigMafTrack.c
@@ -6,30 +6,31 @@
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #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 "hCommon.h"
 #include "hgMaf.h"
 #include "mafTrack.h"
 #include "customTrack.h"
 #include "mafSummary.h"
+#include "quickLift.h"
 #include "mafFrames.h"
 #include "phyloTree.h"
 #include "soTerm.h"
 #include "bigBed.h"
 #include "hubConnect.h"
 #include "chromAlias.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). */
     {
@@ -270,45 +271,90 @@
 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 struct mafAli *quickLiftLoadMafs(struct track *track, int start, int end)
+/* Load MAF blocks out of the assembly the track came from and map them onto the reference. */
+{
+char *liftDb = trackDbSetting(track->tdb, "quickLiftDb");
+char *table = NULL;
+quickLiftResolveTable(track->tdb, track->table, &table, &liftDb);
+char *quickLiftFile = trackDbSetting(track->tdb, "quickLiftUrl");
+
+struct hash *chainHash = newHash(8);
+struct quickLiftRange *range, *rangeList = quickLiftSourceRanges(quickLiftFile, chromName,
+    start, end, chainHash);
+struct mafAli *srcList = NULL;
+
+for (range = rangeList; range != NULL; range = range->next)
+    {
+    struct mafAli *someMafs = NULL;
+    if (track->isBigBed)
+        {
+        struct bbiFile *bbi = fetchBbiForTrack(track);
+        someMafs = bigMafLoadInRegion(bbi, range->chrom, range->start, range->end);
+        bbiFileClose(&bbi);
+        track->bbiFile = NULL;
+        }
+    else
+        {
+        struct sqlConnection *conn = hAllocConn(liftDb);
+        struct sqlConnection *conn2 = hAllocConn(liftDb);
+        someMafs = wigMafLoadInRegion(conn, conn2, table, range->chrom,
+            range->start, range->end, getTrackMafFile(track));
+        hFreeConn(&conn);
+        hFreeConn(&conn2);
+        }
+    srcList = slCat(srcList, someMafs);
+    }
+
+// The browser looks for its reference row by the assembly name without a hub prefix,
+// which is how the maf drawing code builds the name it searches for.
+char refSrc[512];
+safef(refSrc, sizeof refSrc, "%s.%s", hubConnectSkipHubPrefix(database), chromName);
+return quickLiftMafs(chainHash, srcList, liftDb, refSrc,
+                     hChromSize(database, chromName));
+}
+
 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 (inSummaryMode(cart, track->tdb, winBaseCount))
     return;
 
 int begin = winStart - 2;
 if (begin < 0)
     begin = 0;
 
-if (track->isBigBed)
+if (trackDbSetting(track->tdb, "quickLiftDb") != NULL)
+    mp->list = quickLiftLoadMafs(track, begin, winEnd + 2);
+else if (track->isBigBed)
     {
     struct bbiFile *bbi = fetchBbiForTrack(track);
     mp->list = bigMafLoadInRegion(bbi, chromName, begin, winEnd+2);
     bbiFileClose(&bbi);
     track->bbiFile = NULL;
     }
 else if (mp->ct)
     {
 /* 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
@@ -502,31 +548,33 @@
 struct track *wigTrack = track->subtracks;
 int scoreHeight = tl.fontHeight * 4;
 char *snpTable = trackDbSetting(track->tdb, "snpTable");
 boolean doSnpTable = FALSE;
 if ( (track->limitedVis == tvPack) && (snpTable != NULL) && 
     cartOrTdbBoolean(cart, track->tdb, MAF_SHOW_SNP,FALSE))
     doSnpTable = TRUE;
 
 // the maf's only get loaded if we're not in summary or snpTable views
 if (!doSnpTable && !inSummaryMode(cart, track->tdb, winBaseCount))
     {
     /* "close in" display uses actual alignments from file */
     struct mafPriv *mp = getMafPriv(track);
     struct sqlConnection *conn, *conn2;
 
-    if (track->isBigBed)
+    if (trackDbSetting(track->tdb, "quickLiftDb") != NULL)
+        mp->list = quickLiftLoadMafs(track, winStart, winEnd);
+    else if (track->isBigBed)
         {
         struct bbiFile *bbi = fetchBbiForTrack(track);
         mp->list = bigMafLoadInRegion(fetchBbiForTrack(track), chromName, winStart, winEnd);
         bbiFileClose(&bbi);
         track->bbiFile = NULL;
         }
     else 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);