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; }