80daf93864f5609f6b7695766a78edd90cac0c1d
braney
  Fri Jul 31 12:29:05 2026 -0700
quickLift: accept broadPeak tracks, and make lifted peak details right. refs #37970

Add broadPeak to the types validateOneTdb will lift; hgTracks already knew
how to load one.  Make hgc's encodePeak handler read the table out of the
source assembly and lift the items, instead of looking in the destination
assembly where the table does not exist and coming up empty.  Lift peaks with
a new quickLiftPeaks() rather than casting them to bed and calling
quickLiftBeds(), whose thickStart and thickEnd assignments land on
signalValue and pValue in struct encodePeak.

diff --git src/hg/lib/quickLift.c src/hg/lib/quickLift.c
index b91c370206d..03984ea5535 100644
--- src/hg/lib/quickLift.c
+++ src/hg/lib/quickLift.c
@@ -12,30 +12,31 @@
 #include "basicBed.h"
 #include "liftOver.h"
 #include "hash.h"
 #include "bigBed.h"
 #include "bbiFile.h"
 #include "chainNetDbLoad.h"
 #include "hdb.h"
 #include "jksql.h"
 #include "hgConfig.h"
 #include "quickLift.h"
 #include "genePredReader.h"
 #include "bigChain.h"
 #include "bigLink.h"
 #include "chromAlias.h"
 #include "customTrack.h"
+#include "encode/encodePeak.h"
 
 struct bigBedInterval *quickLiftGetIntervals(char *quickLiftFile, struct bbiFile *bbi,   char *chrom, int start, int end, struct hash **pChainHash)
 /* Return intervals from "other" species that will map to the current window.
  * These intervals are NOT YET MAPPED to the current assembly.
  */
 {
 char *linkFileName = bigChainGetLinkFile(quickLiftFile);
 int maxGapBefore = 0;
 int maxGapAfter = 0;
 struct chain *chain, *chainList = chainLoadIdRangeHub(NULL, quickLiftFile, linkFileName, chrom, start, end, -1);
 struct lm *lm = lmInit(0);
 struct bigBedInterval *bbList = NULL, *bb;
 
 for(chain = chainList; chain; chain = chain->next)
     {
@@ -331,30 +332,54 @@
         // probably this should keep track of cases where the input does NOT have thickStart == chromStart
         bed->thickStart = bed->chromStart;
         bed->thickEnd = bed->chromEnd;
         }
     else
         error = remapBlockedBed(chainHash, bed, 0.0, 0.1, TRUE, TRUE, NULL, NULL);
 
     if (error == NULL)
         {
         slAddHead(&liftedBedList, bed);
         }
     }
 return liftedBedList;
 }
 
+struct encodePeak *quickLiftPeaks(struct encodePeak *peakList, struct hash *chainHash)
+// Map a list of encodePeaks in query coordinates to our current reference.  These can't go
+// through quickLiftBeds:  the thickStart and thickEnd it assigns overlay signalValue and
+// pValue in struct encodePeak.
+{
+struct encodePeak *liftedList = NULL;
+struct encodePeak *nextPeak;
+struct encodePeak *peak;
+for(peak = peakList; peak; peak = nextPeak)
+    {
+    nextPeak = peak->next;
+    peak->next = NULL;
+
+    char *error = liftOverRemapRange(chainHash, 0.0, peak->chrom, peak->chromStart, peak->chromEnd,
+                            peak->strand[0],
+                            0.001, &peak->chrom, (int *)&peak->chromStart, (int *)&peak->chromEnd,
+                            &peak->strand[0]);
+
+    if (error == NULL)
+        slAddHead(&liftedList, peak);
+    }
+return liftedList;
+}
+
 boolean quickLiftEnabled(struct cart *cart)
 /* Return TRUE if feature is available */
 {
 char *cfgEnabled = cartOrCfgOption(cart, "browser.quickLift");
 return cfgEnabled && (sameString(cfgEnabled, "on") || sameString(cfgEnabled, "true")) ;
 }
 
 static int hrCmp(const void *va, const void *vb)
 /* Compare to sort based on chromStart. */
 {
 const struct quickLiftRegions *a = *((struct quickLiftRegions **)va);
 const struct quickLiftRegions *b = *((struct quickLiftRegions **)vb);
 return a->chromStart - b->chromStart;
 }