b53f0365865b5449ef209ea86c5220dd07dc8b28
braney
  Fri Aug 7 12:07:45 2026 -0700
quickLift: don't report items that failed to lift as filtered out, refs #38033

Items that could not be mapped through the chain were counted by the same
variable the track filters use, so a lifted view claimed a number of items had
been filtered out when no filter had removed anything.  Count the two cases
apart: failing a filter is a filter rejection, while passing the filters and
then getting nothing back from the lift is a lift failure, and say so in the
label.

diff --git src/hg/hgTracks/bigBedTrack.c src/hg/hgTracks/bigBedTrack.c
index 4431139ac4b..9fcef60b3b3 100644
--- src/hg/hgTracks/bigBedTrack.c
+++ src/hg/hgTracks/bigBedTrack.c
@@ -785,34 +785,38 @@
     mouseOverPattern = cartOrTdbString(cart, track->tdb, "mouseOver", NULL);
 
     if (mouseOverPattern)
         {
         AllocArray(fieldNames, bbi->fieldCount);
         struct slName *field = NULL, *fields = bbFieldNames(bbi);
         int i =  0;
         for (field = fields; field != NULL; field = field->next)
             fieldNames[i++] = field->name;
         }
     }
 
 // a fake item that is the union of the items that span the current  window
 struct linkedFeatures *spannedLf = NULL;
 unsigned filtered = 0;
+unsigned notLifted = 0;
 struct bed *bed = NULL, *bedCopy = NULL;
 for (bb = bbList; bb != NULL; bb = bb->next)
     {
     struct linkedFeatures *lf = NULL;
+    // an item that passed the filters but has no clean mapping through the chain was
+    // dropped by the lift, not by a filter, and needs to be reported in its own words
+    boolean liftFailed = FALSE;
     bedCopy = NULL;
     char *bedRow[bbi->fieldCount];
     if (sameString(track->tdb->type, "bigPsl"))
         {
         // fill out bedRow to support mouseOver pattern replacements
         char startBuf[16], endBuf[16];
         bigBedIntervalToRow(bb, chromName, startBuf, endBuf, bedRow, ArraySize(bedRow));
         char *seq, *cds;
         struct psl *psl = pslFromBigPsl(chromName, bb, seqTypeField,  &seq, &cds);
         int sizeMul =  pslIsProtein(psl) ? 3 : 1;
         boolean isXeno = 0;  // just affects grayIx
         boolean nameGetsPos = FALSE; // we want the name to stay the name
 
         if (sizeMul == 3)
             {
@@ -841,30 +845,32 @@
         }
     else
 	{
         char startBuf[16], endBuf[16];
         bigBedIntervalToRow(bb, chromName, startBuf, endBuf, bedRow, ArraySize(bedRow));
         if (bigBedFilterInterval(bbi, bedRow, filters))
             {
             if (quickLiftFile)
                 {
                 if ((bed = quickLiftIntervalsToBed(bbi, chainHash, bb)) != NULL)
                     {
                     bedCopy = cloneBed(bed);
                     lf = bedMungToLinkedFeatures(&bed, tdb, fieldCount,
                         scoreMin, scoreMax, useItemRgb);
                     }
+                else
+                    liftFailed = TRUE;
                 }
             else
                 {
                 bed = bedLoadN(bedRow, fieldCount == 7 ? 6 : fieldCount);
                 bedCopy = cloneBed(bed);
                 lf = bedMungToLinkedFeatures(&bed, tdb, fieldCount,
                     scoreMin, scoreMax, useItemRgb);
                 }
             }
 
         if (lf && highlights)
             addHighlightToLinkedFeature(lf, highlights, bbi, bedRow, track->tdb);
 
         if (lf && squishFieldIdx)
             lf->squishyPackVal = atof(restField(bb, squishFieldIdx));
@@ -921,30 +927,33 @@
                 else if (mouseOverPattern)
                     {
                     char qStartBuf[16], qEndBuf[16];
                     if (quickLiftFile && bedCopy)
                         quickLiftSetCoordFields(bedRow, bedCopy, qStartBuf, qEndBuf, sizeof qStartBuf);
                     tmp->mouseOver = replaceFieldInPattern(mouseOverPattern, bbi->fieldCount, fieldNames, bedRow);
                     }
                 slAddHead(&spannedLf, tmp);
                 }
             continue; // lf will be NULL, but these items aren't "filtered", they're merged
             }
 	}
 
     if (lf == NULL)
         {
+        if (liftFailed)
+            notLifted++;
+        else
             filtered++;
         continue;
         }
 
     if (lf->label == NULL)
         lf->label = bigBedMakeLabel(track->tdb, track->labelColumns,  bb, chromName);
     if (startsWith("bigGenePred", track->tdb->type) || startsWith("genePred", track->tdb->type))
         {
         // bedRow[5] has original strand in it, bedCopy has new strand.  If they're different we want to reverse exonFrames
         boolean changedStrand = FALSE;
         if (quickLiftFile)
             {
             if (*bedRow[5] != *bedCopy->strand)
                 changedStrand = TRUE;
             }
@@ -968,30 +977,33 @@
             lf->mouseOver = restField(bb, mouseOverIdx);
         else if (mouseOverPattern)
             {
             char qStartBuf[16], qEndBuf[16];
             if (quickLiftFile && bedCopy)
                 quickLiftSetCoordFields(bedRow, bedCopy, qStartBuf, qEndBuf, sizeof qStartBuf);
             lf->mouseOver = replaceFieldInPattern(mouseOverPattern, bbi->fieldCount, fieldNames, bedRow);
             }
         }
     slAddHead(pLfList, lf);
     }
 
 if (filtered)
    labelTrackAsFilteredNumber(track, filtered);
 
+if (notLifted)
+   track->longLabel = labelAsNotLiftedNumber(track->longLabel, notLifted);
+
 if (doWindowSizeFilter)
     // add the number of merged items to the track longLabel
     {
     char labelBuf[256];
     if (mergeCount > 1)
         safef(labelBuf, sizeof(labelBuf), " (Merged %d items)", mergeCount);
     else
         safef(labelBuf, sizeof(labelBuf), " (No Items Merged in window)");
     track->longLabel = catTwoStrings(track->longLabel, labelBuf);
     }
 
 if (spannedLf)
     {
     // if two or more items were merged together, fix up the label of the special merged item,
     // otherwise the label and mouseOver will be the normal bed one