2521d696f5073ce8cee3f59f423f161fdb77d550
max
  Fri Sep 25 02:48:37 2026 -0700
VCF tracks: the bigBed trackDb filters (filter.*, filterByRange, filterLimits, filterValues, filterType, filterText, filterLabel) now work on INFO fields, plus ID and QUAL. The field list and types come from the VCF header; the bigBed filter code is reused, bigBed behavior unchanged, refs #37617

diff --git src/hg/hgTracks/bigBedTrack.c src/hg/hgTracks/bigBedTrack.c
index fe193402f79..60bd0ed871b 100644
--- src/hg/hgTracks/bigBedTrack.c
+++ src/hg/hgTracks/bigBedTrack.c
@@ -33,36 +33,37 @@
 #include "hgConfig.h"
 #include "heatmap.h"
 
 static unsigned getFieldNum(struct bbiFile *bbi, char *field)
 // get field number for field name in bigBed.  errAbort if field not found.
 {
 int fieldNum =  bbFieldIndex(bbi, field);
 if (fieldNum < 0)
     fieldNum = defaultFieldLocation(field);
 if (fieldNum < 0)
     errAbort("error building filter with field %s.  Field not found.", field);
 
 return fieldNum;
 }
 
-struct bigBedFilter *bigBedMakeNumberFilter(struct cart *cart, struct bbiFile *bbi, struct trackDb *tdb, char *filter, char *defaultLimits,  char *field, boolean isHighlight)
-/* Make a filter/highlight on this column if the trackDb or cart wants us to. */
+struct bigBedFilter *bigBedMakeNumberFilterOnField(struct cart *cart, struct trackDb *tdb, char *filter, char *defaultLimits,  char *field, int fieldNum, boolean isHighlight)
+/* Make a filter/highlight on field number fieldNum if the trackDb or cart wants us to.
+ * Like bigBedMakeNumberFilter, but the caller has already resolved the field number, so
+ * this can also be used on things that are not bigBed rows, e.g. VCF INFO fields. */
 {
 struct bigBedFilter *ret = NULL;
 char *setting = trackDbSettingClosestToHome(tdb, filter);
-int fieldNum =  getFieldNum(bbi, field);
 if (setting)
     {
     boolean invalid = FALSE;
     double minValueTdb = 0,maxValueTdb = NO_VALUE;
     double minLimit=NO_VALUE,maxLimit=NO_VALUE,min = minValueTdb,max = maxValueTdb;
     colonPairToDoubles(setting,&minValueTdb,&maxValueTdb);
     colonPairToDoubles(defaultLimits,&minLimit,&maxLimit);
     getScoreFloatRangeFromCart(cart,tdb,FALSE,filter,&minLimit,&maxLimit,&min,&max);
     if ((int)minLimit != NO_VALUE || (int)maxLimit != NO_VALUE)
         {
         // assume tdb default values within range!
         // (don't give user errors that have no consequence)
         if ((min != minValueTdb && (((int)minLimit != NO_VALUE && min < minLimit)
                                 || ((int)maxLimit != NO_VALUE && min > maxLimit)))
         ||  (max != maxValueTdb && (((int)minLimit != NO_VALUE && max < minLimit)
@@ -110,117 +111,142 @@
             ret->value1 = max;
             }
         else
             {
             ret->comparisonType = COMPARE_BETWEEN;
             ret->value1 = min;
             ret->value2 = max;
             }
         if (isHighlight)
             ret->isHighlight = TRUE;
         }
     }
 return ret;
 }
 
-struct bigBedFilter *bigBedMakeFilterText(struct cart *cart, struct bbiFile *bbi, struct trackDb *tdb, char *filterName, char *field, boolean isHighlight)
-/* Add a bigBed filter using a trackDb filterText statement. */
+struct bigBedFilter *bigBedMakeNumberFilter(struct cart *cart, struct bbiFile *bbi, struct trackDb *tdb, char *filter, char *defaultLimits,  char *field, boolean isHighlight)
+/* Make a filter/highlight on this column if the trackDb or cart wants us to. */
+{
+int fieldNum =  getFieldNum(bbi, field);
+return bigBedMakeNumberFilterOnField(cart, tdb, filter, defaultLimits, field, fieldNum, isHighlight);
+}
+
+struct bigBedFilter *bigBedMakeFilterTextOnField(struct cart *cart, struct trackDb *tdb, char *filterName, char *field, int fieldNum, boolean isHighlight)
+/* Add a filter using a trackDb filterText statement on field number fieldNum. */
 {
 struct bigBedFilter *filter;
 char *setting = trackDbSettingClosestToHome(tdb, filterName);
 char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, filterName, setting);
 
 if (isEmpty(value)) 
     return NULL;
 
 char *typeValue = getFilterType(cart, tdb, field, FILTERTEXT_WILDCARD);
 
 AllocVar(filter);
-filter->fieldNum =  getFieldNum(bbi, field);
+filter->fieldNum = fieldNum;
 
 if (sameString(typeValue, FILTERTEXT_REGEXP) )
     {
     filter->comparisonType = COMPARE_REGEXP;
     regcomp(&filter->regEx, value, REG_NOSUB);
     }
 else
     {
     filter->comparisonType = COMPARE_WILDCARD;
     filter->wildCardString = cloneString(value);
     }
 
 filter->isHighlight = isHighlight;
 
 return filter;
 }
 
+struct bigBedFilter *bigBedMakeFilterText(struct cart *cart, struct bbiFile *bbi, struct trackDb *tdb, char *filterName, char *field, boolean isHighlight)
+/* Add a bigBed filter using a trackDb filterText statement. */
+{
+char *setting = trackDbSettingClosestToHome(tdb, filterName);
+char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, filterName, setting);
+if (isEmpty(value))
+    return NULL;
+int fieldNum =  getFieldNum(bbi, field);
+return bigBedMakeFilterTextOnField(cart, tdb, filterName, field, fieldNum, isHighlight);
+}
+
 char *getHighlightType(struct cart *cart, struct trackDb *tdb, char *field, char *def)
 {
 char settingString[4096];
 safef(settingString, sizeof settingString, "%s.%s", HIGHLIGHT_TYPE_NAME_LOW, field);
 char *setting = cartOrTdbString(cart, tdb, settingString, NULL);
 if (setting == NULL)
     {
     safef(settingString, sizeof settingString, "%s.%s", field, HIGHLIGHT_TYPE_NAME_CAP);
     setting = cartOrTdbString(cart, tdb, settingString, NULL);
     }
 if (setting == NULL)
     {
     safef(settingString, sizeof settingString, "%s%s", field, HIGHLIGHT_TYPE_NAME_CAP);
     setting = cartOrTdbString(cart, tdb, settingString, def);
     }
 return setting;
 }
 
-struct bigBedFilter *bigBedMakeFilterBy(struct cart *cart, struct bbiFile *bbi, struct trackDb *tdb, char *field, struct slName *choices, boolean isHighlight)
-/* Add a bigBed filter using a trackDb filterBy statement. */
+struct bigBedFilter *bigBedMakeFilterByOnField(struct cart *cart, struct trackDb *tdb, char *field, int fieldNum, struct slName *choices, boolean isHighlight)
+/* Add a filter using a trackDb filterBy/filterValues statement on field number fieldNum. */
 {
 struct bigBedFilter *filter;
 char *setting = NULL;
 if (isHighlight)
     setting = getHighlightType(cart, tdb, field, HIGHLIGHTBY_DEFAULT);
 else
 setting = getFilterType(cart, tdb, field,  FILTERBY_DEFAULT);
 
 AllocVar(filter);
-filter->fieldNum =  getFieldNum(bbi, field);
+filter->fieldNum = fieldNum;
 filter->comparisonType = COMPARE_HASH;
 if (setting) 
     {
     if (sameString(setting, FILTERBY_SINGLE_LIST) 
             || sameString(setting, FILTERBY_MULTIPLE_LIST_OR)
             || sameString(setting, FILTERBY_MULTIPLE_LIST_ONLY_OR)
             || sameString(setting, HIGHLIGHTBY_SINGLE_LIST)
             || sameString(setting, HIGHLIGHTBY_MULTIPLE_LIST_OR)
             || sameString(setting, HIGHLIGHTBY_MULTIPLE_LIST_ONLY_OR))
                 filter->comparisonType = COMPARE_HASH_LIST_OR;
     else if (sameString(setting, FILTERBY_MULTIPLE_LIST_AND) 
             || sameString(setting, FILTERBY_MULTIPLE_LIST_ONLY_AND)
             || sameString(setting, HIGHLIGHTBY_MULTIPLE_LIST_AND)
             || sameString(setting, HIGHLIGHTBY_MULTIPLE_LIST_ONLY_AND))
                 filter->comparisonType = COMPARE_HASH_LIST_AND;
     }
 filter->valueHash = newHash(5);
 filter->numValuesInHash = slCount(choices);
 
 for(; choices; choices = choices->next)
     hashStore(filter->valueHash, choices->name);
 
 filter->isHighlight = isHighlight;
 return filter;
 }
 
+struct bigBedFilter *bigBedMakeFilterBy(struct cart *cart, struct bbiFile *bbi, struct trackDb *tdb, char *field, struct slName *choices, boolean isHighlight)
+/* Add a bigBed filter using a trackDb filterBy statement. */
+{
+int fieldNum =  getFieldNum(bbi, field);
+return bigBedMakeFilterByOnField(cart, tdb, field, fieldNum, choices, isHighlight);
+}
+
 static void addGencodeFilters(struct cart *cart, struct trackDb *tdb, struct bigBedFilter **pFilters)
 /* Add GENCODE custom bigBed filters. */
 {
 struct bigBedFilter *filter;
 char varName[64];
 struct hash *hash;
 
 /* canonical */
 safef(varName, sizeof(varName), "%s.show.spliceVariants", tdb->track);
 boolean option = cartUsualBoolean(cart, varName, TRUE);
 if (!option)
     {
     AllocVar(filter);
     slAddHead(pFilters, filter);
     filter->fieldNum = 25;
@@ -345,97 +371,104 @@
 
 filterBy_t *filterBySet = highlightBySetGet(tdb, cart,NULL);
 filterBy_t *filterBy = filterBySet;
 for (;filterBy != NULL; filterBy = filterBy->next)
     {
     if (filterBy->slChoices && differentString(filterBy->slChoices->name, "All"))
         {
         if ((highlight = bigBedMakeFilterBy(cart, bbi, tdb, filterBy->column, filterBy->slChoices, TRUE)) != NULL)
             slAddHead(&highlights, highlight);
         }
     }
 
 return highlights;
 }
 
-boolean bigBedFilterOne(struct bigBedFilter *filter, char **bedRow, struct bbiFile *bbi)
-/* Return TRUE if a bedRow passes one filter or is in hgFindMatches */
+boolean bigBedFilterOneValue(struct bigBedFilter *filter, char *fieldVal)
+/* Return TRUE if the string value of a field passes one filter. The field number
+ * in the filter is not used, the caller has already picked the field. */
 {
-if ((bbi->definedFieldCount > 3) && (hgFindMatches != NULL) && 
-    (bedRow[3] != NULL)  && hashLookup(hgFindMatches, bedRow[3]) != NULL)
-    return TRUE;
-
-double val = atof(bedRow[filter->fieldNum]);
+double val = atof(fieldVal);
 
 switch(filter->comparisonType)
     {
     case COMPARE_WILDCARD:
-        if ( !wildMatch(filter->wildCardString, bedRow[filter->fieldNum]))
+        if ( !wildMatch(filter->wildCardString, fieldVal))
             return FALSE;
         break;
     case COMPARE_REGEXP:
-        if (regexec(&filter->regEx,bedRow[filter->fieldNum], 0, NULL,0 ) != 0)
+        if (regexec(&filter->regEx,fieldVal, 0, NULL,0 ) != 0)
             return FALSE;
         break;
     case COMPARE_HASH_LIST_AND:
     case COMPARE_HASH_LIST_OR:
         {
-        struct slName *values = commaSepToSlNames(bedRow[filter->fieldNum]);
+        struct slName *values = commaSepToSlNames(fieldVal);
         unsigned found = 0;
         struct hash *seenHash = newHash(3);
         for(; values; values = values->next)
             {
             if (hashLookup(seenHash, values->name))
                 continue;
             hashStore(seenHash, values->name);
             if (hashLookup(filter->valueHash, values->name))
                 {
                 found++;
                 if (filter->comparisonType == COMPARE_HASH_LIST_OR) 
                     break;
                 }
             }
         if (filter->comparisonType == COMPARE_HASH_LIST_AND) 
             {
             if (found < filter->numValuesInHash)
                 return FALSE;
             }
         else if (!found)
             return FALSE;
         }
         break;
 
     case COMPARE_HASH:
-        if (!hashLookup(filter->valueHash, bedRow[filter->fieldNum]))
+        if (!hashLookup(filter->valueHash, fieldVal))
             return FALSE;
         break;
     case COMPARE_LESS:
         if (!(val <= filter->value1))
             return FALSE;
         break;
     case COMPARE_MORE:
         if (!(val >= filter->value1))
             return FALSE;
         break;
     case COMPARE_BETWEEN:
         if (!((val >= filter->value1) && (val <= filter->value2)))
             return FALSE;
         break;
     }
 return TRUE;
 }
 
+boolean bigBedFilterOne(struct bigBedFilter *filter, char **bedRow, struct bbiFile *bbi)
+/* Return TRUE if a bedRow passes one filter or is in hgFindMatches */
+{
+if ((bbi->definedFieldCount > 3) && (hgFindMatches != NULL) && 
+    (bedRow[3] != NULL)  && hashLookup(hgFindMatches, bedRow[3]) != NULL)
+    return TRUE;
+
+return bigBedFilterOneValue(filter, bedRow[filter->fieldNum]);
+}
+
 
 boolean bigBedFilterInterval(struct bbiFile *bbi, char **bedRow, struct bigBedFilter *filters)
 /* Go through a row and filter based on filters.  Return TRUE if all filters are passed. */
 {
 if ((bbi->definedFieldCount > 3) && (hgFindMatches != NULL) && 
     (bedRow[3] != NULL)  && hashLookup(hgFindMatches, bedRow[3]) != NULL)
     return TRUE;
 
 struct bigBedFilter *filter;
 for(filter = filters; filter; filter = filter->next)
     if (!bigBedFilterOne(filter, bedRow, bbi))
         return FALSE;
 return TRUE;
 }