81d00f3eec6ea6978c9a71ed6a48c84a0bd0c987
braney
  Wed Apr 22 14:51:08 2026 -0700
hgFind: remap bigBed search hits from source to destination coords when the track is quickLifted. Previously a search for e.g. "BRCA2" on a quickLifted hub (hg38 tracks displayed on HG02257.pat) returned hits at hg38 chr13 coordinates; clicking the result errored with "Sorry, couldn't locate chr13:... in <dest>". Adds quickLiftLiftPos() in hg/lib/quickLift.c, which reads the source->dest liftOverChainFile and calls liftOverRemapRange. Called from bigBedIntervalListToHgPositions in hg/lib/bigBedFind.c whenever tdb has quickLiftUrl/quickLiftDb; hits that don't map through the chain are dropped. refs #36340

diff --git src/hg/lib/quickLift.c src/hg/lib/quickLift.c
index 0318286fb9b..a74ba6adf10 100644
--- src/hg/lib/quickLift.c
+++ src/hg/lib/quickLift.c
@@ -513,15 +513,51 @@
     NULL, extraWhere, loader, numFields, chainHash);
 struct bed *liftedBeds = quickLiftBeds(bed, chainHash, blocked);
 hFreeConn(&conn);
 return liftedBeds;
 }
 
 char *quickLiftChainTable()
 /* Return the name of the quickLiftChain table. */
 {
 static char *quickLiftChainTable = NULL;
 if (quickLiftChainTable == NULL)
     quickLiftChainTable = cfgOptionEnvDefault("QUICKLIFTCHAINNAME",
 	    quickLiftChainTableConfVariable, defaultQuickLiftChainTableName);
 return quickLiftChainTable;
 }
+
+boolean quickLiftLiftPos(char *sourceDb, char *destDb,
+    char *chrom, int start, int end,
+    char **retChrom, int *retStart, int *retEnd)
+/* Map a position from source (sourceDb) coords to destination (destDb) coords
+ * using the liftOver chain for sourceDb -> destDb.  This is used to remap
+ * hgFind results from quickLifted bigBed tracks (which return hits in the
+ * source assembly's coordinates) back to the destination assembly the user
+ * is viewing.  Returns TRUE on success. */
+{
+static struct hash *fileToChainHash = NULL;
+if (fileToChainHash == NULL)
+    fileToChainHash = newHash(0);
+
+char key[1024];
+safef(key, sizeof(key), "%s->%s", sourceDb, destDb);
+struct hash *chainHash = hashFindVal(fileToChainHash, key);
+if (chainHash == NULL)
+    {
+    char *chainFile = liftOverChainFile(sourceDb, destDb);
+    if (chainFile == NULL)
+        return FALSE;
+    chainHash = newHash(0);
+    // This reads every chain in the file up front.  A bigChain-format
+    // liftOver chain indexed on the source (fromDb) side would let us
+    // load just the chains overlapping the hit; worth revisiting if the
+    // upfront cost becomes an issue.
+    readLiftOverMap(chainFile, chainHash);
+    hashAdd(fileToChainHash, key, chainHash);
+    }
+
+char strand = '+';
+char *error = liftOverRemapRange(chainHash, 0.0, chrom, start, end, strand,
+                                 0.001, retChrom, retStart, retEnd, &strand);
+return (error == NULL);
+}