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 ". 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); +}