00811fcbcfd1c9a317b07477ff46a14dba046160 braney Wed Sep 9 12:39:44 2026 -0700 bigBed: use the file's own field count when the type line asks for more, refs #38310 A `type bigBed N` larger than the number of fields the file holds left the track drawing nothing at all, and its details page reporting a disagreement instead of the item. Fall back on the count in the file's header, which is the count hubCheck already requires the type line to match. The bound has to be the file's total field count and not its definedFieldCount. Fifty-two tracks legitimately declare more bed fields than their header calls defined, forty-eight of them the hs1 T2T_Encode_LOPeaks narrowPeak set, and those are untouched. Three tracks are in the over-declared state today, all `type bigBed 4` over a three-field file: hg38 setDups, and the KAPA_HyperExome and nexterarapidcapture subtracks of hg19 exomeProbesets. Their item boxes render pixel-identically and their details pages, which failed before, now work. Co-Authored-By: Claude Opus 5 (1M context) diff --git src/hg/hgTracks/bigBedTrack.c src/hg/hgTracks/bigBedTrack.c index c5cdac79997..fe193402f79 100644 --- src/hg/hgTracks/bigBedTrack.c +++ src/hg/hgTracks/bigBedTrack.c @@ -1,1170 +1,1175 @@ /* bigBed - stuff to handle loading and display of bigBed type tracks in browser. * Mostly just links to bed code, but handles a few things itself, like the dense * drawing code. */ /* Copyright (C) 2014 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include "common.h" #include "hash.h" #include "linefile.h" #include "jksql.h" #include "hdb.h" #include "bedCart.h" #include "hgTracks.h" #include "hmmstats.h" #include "localmem.h" #include "wigCommon.h" #include "bbiFile.h" #include "obscure.h" #include "bigWig.h" #include "bigBed.h" #include "bigWarn.h" #include "errCatch.h" #include "trackHub.h" #include "net.h" #include "bigPsl.h" #include "bPlusTree.h" // for the chrom-name key size of the bigBed we are reading #include "bigBedFilter.h" #include "bigBedLabel.h" #include "variation.h" #include "chromAlias.h" #include "quickLift.h" #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 *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) || ((int)maxLimit != NO_VALUE && max > maxLimit)))) { invalid = TRUE; char value[64]; if ((int)max == NO_VALUE) // min only is allowed, but max only is not safef(value, sizeof(value), "entered minimum (%g)", min); else safef(value, sizeof(value), "entered range (min:%g and max:%g)", min, max); char limits[64]; if ((int)minLimit != NO_VALUE && (int)maxLimit != NO_VALUE) safef(limits, sizeof(limits), "violates limits (%g to %g)", minLimit, maxLimit); else if ((int)minLimit != NO_VALUE) safef(limits, sizeof(limits), "violates lower limit (%g)", minLimit); else //if ((int)maxLimit != NO_VALUE) safef(limits, sizeof(limits), "violates uppper limit (%g)", maxLimit); warn("invalid filter by %s: %s %s for track %s", field, value, limits, tdb->track); } } if (invalid) { char filterLimitName[64]; safef(filterLimitName, sizeof(filterLimitName), "%s%s", filter, _MIN); cartRemoveVariableClosestToHome(cart,tdb,FALSE,filterLimitName); safef(filterLimitName, sizeof(filterLimitName), "%s%s", filter, _MAX); cartRemoveVariableClosestToHome(cart,tdb,FALSE,filterLimitName); } else if (((int)min != NO_VALUE && ((int)minLimit == NO_VALUE || minLimit != min)) || ((int)max != NO_VALUE && ((int)maxLimit == NO_VALUE || maxLimit != max))) // Assumes min==NO_VALUE or min==minLimit is no filter // Assumes max==NO_VALUE or max==maxLimit is no filter! { AllocVar(ret); ret->fieldNum = fieldNum; if ((int)max == NO_VALUE || ((int)maxLimit != NO_VALUE && maxLimit == max)) { ret->comparisonType = COMPARE_MORE; ret->value1 = min; } else if ((int)min == NO_VALUE || ((int)minLimit != NO_VALUE && minLimit == min)) { ret->comparisonType = COMPARE_LESS; 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 *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); 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; } 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 *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->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; } 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; filter->comparisonType = COMPARE_HASH_LIST_OR; hash = newHash(5); filter->valueHash = hash; filter->numValuesInHash = 1; hashStore(hash, "canonical" ); } /* transcript class */ AllocVar(filter); slAddHead(pFilters, filter); filter->fieldNum = 20; filter->comparisonType = COMPARE_HASH; hash = newHash(5); filter->valueHash = hash; filter->numValuesInHash = 1; hashStore(hash,"coding"); // coding is always included safef(varName, sizeof(varName), "%s.show.noncoding", tdb->track); if (cartUsualBoolean(cart, varName, TRUE)) { filter->numValuesInHash++; hashStore(hash,"nonCoding"); } safef(varName, sizeof(varName), "%s.show.pseudo", tdb->track); if (cartUsualBoolean(cart, varName, FALSE)) { filter->numValuesInHash++; hashStore(hash,"pseudo"); } /* tagged sets */ safef(varName, sizeof(varName), "%s.show.set", tdb->track); char *setString = cartUsualString(cart, varName, "basic"); if (differentString(setString, "all")) { AllocVar(filter); slAddHead(pFilters, filter); filter->fieldNum = 23; filter->comparisonType = COMPARE_HASH_LIST_OR; hash = newHash(5); filter->valueHash = hash; filter->numValuesInHash = 1; hashStore(hash, setString); } } struct bigBedFilter *bigBedBuildFilters(struct cart *cart, struct bbiFile *bbi, struct trackDb *tdb) /* Build all the numeric and filterBy filters for a bigBed */ { struct bigBedFilter *filters = NULL, *filter; struct trackDbFilter *tdbFilters = tdbGetTrackNumFilters(tdb); if ((tdbFilters == NULL) && !trackDbSettingOn(tdb, "noScoreFilter") && (bbi->definedFieldCount >= 5)) { AllocVar(filter); slAddHead(&filters, filter); filter->fieldNum = 4; filter->comparisonType = COMPARE_MORE; char buffer[2048]; safef(buffer, sizeof buffer, "%s.scoreFilter", tdb->track); filter->value1 = cartUsualDouble(cart, buffer, 0.0); } for(; tdbFilters; tdbFilters = tdbFilters->next) { if ((filter = bigBedMakeNumberFilter(cart, bbi, tdb, tdbFilters->name, NULL, tdbFilters->fieldName, FALSE)) != NULL) slAddHead(&filters, filter); } tdbFilters = tdbGetTrackTextFilters(tdb); for(; tdbFilters; tdbFilters = tdbFilters->next) { if ((filter = bigBedMakeFilterText(cart, bbi, tdb, tdbFilters->name, tdbFilters->fieldName, FALSE)) != NULL) slAddHead(&filters, filter); } filterBy_t *filterBySet = filterBySetGet(tdb, cart,NULL); filterBy_t *filterBy = filterBySet; for (;filterBy != NULL; filterBy = filterBy->next) { if (filterBy->slChoices && differentString(filterBy->slChoices->name, "All")) { if ((filter = bigBedMakeFilterBy(cart, bbi, tdb, filterBy->column, filterBy->slChoices, FALSE)) != NULL) slAddHead(&filters, filter); } } /* custom gencode filters */ boolean isGencode3 = trackDbSettingOn(tdb, "isGencode3"); if (isGencode3) addGencodeFilters(cart, tdb, &filters); return filters; } struct bigBedFilter *bigBedBuildHighlights(struct cart *cart, struct bbiFile *bbi, struct trackDb *tdb) /* Build all the numeric and highlights for a bigBed */ { struct bigBedFilter *highlights = NULL, *highlight; struct trackDbFilter *tdbHighlights = tdbGetTrackNumHighlights(tdb); for(; tdbHighlights; tdbHighlights = tdbHighlights->next) { if ((highlight = bigBedMakeNumberFilter(cart, bbi, tdb, tdbHighlights->name, NULL, tdbHighlights->fieldName, TRUE)) != NULL) slAddHead(&highlights, highlight); } tdbHighlights = tdbGetTrackTextHighlights(tdb); for(; tdbHighlights; tdbHighlights = tdbHighlights->next) { if ((highlight = bigBedMakeFilterText(cart, bbi, tdb, tdbHighlights->name, tdbHighlights->fieldName, TRUE)) != NULL) slAddHead(&highlights, highlight); } 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 */ { if ((bbi->definedFieldCount > 3) && (hgFindMatches != NULL) && (bedRow[3] != NULL) && hashLookup(hgFindMatches, bedRow[3]) != NULL) return TRUE; double val = atof(bedRow[filter->fieldNum]); switch(filter->comparisonType) { case COMPARE_WILDCARD: if ( !wildMatch(filter->wildCardString, bedRow[filter->fieldNum])) return FALSE; break; case COMPARE_REGEXP: if (regexec(&filter->regEx,bedRow[filter->fieldNum], 0, NULL,0 ) != 0) return FALSE; break; case COMPARE_HASH_LIST_AND: case COMPARE_HASH_LIST_OR: { struct slName *values = commaSepToSlNames(bedRow[filter->fieldNum]); 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])) 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 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; } struct bbiFile *fetchBbiForTrack(struct track *track) /* Fetch bbiFile from track, opening it if it is not already open. */ { struct bbiFile *bbi = track->bbiFile; if (bbi == NULL) { char *fileName = NULL; if (track->parallelLoading) // do not use mysql during parallel fetch { fileName = hReplaceGbdb(trackDbSetting(track->tdb, "bigDataUrl")); if (fileName == NULL) fileName = hReplaceGbdb(trackDbSetting(track->tdb, "bigGeneDataUrl")); } else { struct sqlConnection *conn = NULL; if (!trackHubDatabase(database)) conn = hAllocConnTrack(database, track->tdb); fileName = bbiNameFromSettingOrTable(track->tdb, conn, track->table); hFreeConn(&conn); } #ifdef USE_GBIB_PWD #include "gbib.c" #endif bbi = track->bbiFile = bigBedFileOpenAlias(fileName, chromAliasFindAliases); } return bbi; } static unsigned bigBedMaxItems() /* Get the maximum number of items to grab from a bigBed file. Defaults to ten thousand . */ { static boolean set = FALSE; static unsigned maxItems = 0; if (!set) { char *maxItemsStr = cfgOptionDefault("bigBedMaxItems", "10000"); maxItems = sqlUnsigned(maxItemsStr); set = TRUE; } return maxItems; } static boolean hasOverflowedInWindow(struct track *track) /* has it overlowed in the given track? */ { uint resultCount = 0; // do not use -1, it messes up the compare of signed resultsCount with unsigned bigMaxItems. char *resultCountString = trackDbSetting(track->tdb, "bigBedItemsCount"); if (resultCountString) resultCount = sqlUnsigned(resultCountString); return (resultCount > bigBedMaxItems()); } static void loadBigBedSummary(struct track *track) /* Check if summary loading needed for bigBed */ { if (track->subtracks) // do tracks or subtracks but not parents. { return; } struct lm *lm = lmInit(0); char *chrom = chromName; int start = winStart; int end = winEnd; /* protect against temporary network error */ struct errCatch *errCatch = errCatchNew(); boolean filtering = FALSE; // for the moment assume we're not filtering if (errCatchStart(errCatch)) { // scan all windows for errors and overflows boolean errorsInWindows = FALSE; boolean overFlowedInWindows = FALSE; struct track *thisTrack; for(thisTrack=track->prevWindow; thisTrack; thisTrack=thisTrack->prevWindow) { if (hasOverflowedInWindow(thisTrack)) overFlowedInWindows = TRUE; if (thisTrack->drawItems == bigDrawWarning) errorsInWindows = TRUE; } for(thisTrack=track->nextWindow; thisTrack; thisTrack=thisTrack->nextWindow) { if (hasOverflowedInWindow(thisTrack)) overFlowedInWindows = TRUE; if (thisTrack->drawItems == bigDrawWarning) errorsInWindows = TRUE; } if (hasOverflowedInWindow(track)) overFlowedInWindows = TRUE; if (!errorsInWindows && overFlowedInWindows) { if (filtering) errAbort("Too many items in window to filter.Zoom in or remove filters to view track."); else { struct bbiFile *bbi = fetchBbiForTrack(track); if (bbi) { // use summary levels if (track->visibility != tvDense) { track->limitedVis = tvFull; track->limitWiggle = TRUE; track->limitedVisSet = TRUE; } else { track->limitedVis = tvDense; track->limitedVisSet = TRUE; } AllocArray(track->summary, insideWidth); if (bigBedSummaryArrayExtended(bbi, chrom, start, end, insideWidth, track->summary)) { char *denseCoverage = trackDbSettingClosestToHome(track->tdb, "denseCoverage"); if (denseCoverage != NULL) { double endVal = atof(denseCoverage); if (endVal <= 0) { AllocVar(track->sumAll); *track->sumAll = bbiTotalSummary(bbi); } } } else freez(&track->summary); } } } } errCatchEnd(errCatch); if (errCatch->gotError) { track->networkErrMsg = cloneString(errCatch->message->string); track->drawItems = bigDrawWarning; track->totalHeight = bigWarnTotalHeight; } errCatchFree(&errCatch); lmCleanup(&lm); track->bbiFile = NULL; } struct bigBedInterval *bigBedSelectRangeExt(struct track *track, char *chrom, int start, int end, struct lm *lm, int maxItems) /* Return list of intervals in range. */ { struct bigBedInterval *result = NULL; /* protect against temporary network error */ struct errCatch *errCatch = errCatchNew(); if (errCatchStart(errCatch)) { struct bbiFile *bbi = fetchBbiForTrack(track); result = bigBedIntervalQuery(bbi, chrom, start, end, bigBedMaxItems() + 1, lm); // pass in desired limit or 0 for all. char resultCount[32]; safef(resultCount, sizeof resultCount, "%u", slCount(result)); trackDbAddSetting(track->tdb, "bigBedItemsCount", resultCount); if (slCount(result) > bigBedMaxItems()) { result = NULL; // IS Having it return NULL a critical part of this? } track->bbiFile = NULL; } errCatchEnd(errCatch); if (errCatch->gotError) { track->networkErrMsg = cloneString(errCatch->message->string); track->drawItems = bigDrawWarning; track->totalHeight = bigWarnTotalHeight; result = NULL; } errCatchFree(&errCatch); return result; } char* restField(struct bigBedInterval *bb, int fieldIdx) /* return a given field from the bb->rest field, NULL on error */ { if (fieldIdx==0) // we don't return the first(=name) field of bigBed return NULL; char *rest = cloneString(bb->rest); char *restFields[1024]; int restCount = chopTabs(rest, restFields); char *field = NULL; if (fieldIdx < restCount) field = cloneString(restFields[fieldIdx]); freeMem(rest); return field; } void addHighlightToLinkedFeature(struct linkedFeatures *lf, struct bigBedFilter *highlights, struct bbiFile *bbi, char **bedRow, struct trackDb *tdb) /* Fill out the lf->highlightColor if the cart says to highlight this item. The color will * be the 'average' of all the highlight colors specified */ { struct bigBedFilter *highlight; char *cartHighlightColor = cartOrTdbString(cart, tdb, HIGHLIGHT_COLOR_CART_VAR, HIGHLIGHT_COLOR_DEFAULT); for (highlight = highlights; highlight != NULL; highlight = highlight->next) { if (bigBedFilterOne(highlight, bedRow, bbi)) { unsigned rgb = bedParseColor(cartHighlightColor); Color color = bedColorToGfxColor(rgb); lf->highlightColor = color; lf->highlightMode = highlightBackground; } } } static void quickLiftSetCoordFields(char **bedRow, struct bed *liftedBed, char *startBuf, char *endBuf, int bufSize) /* Overwrite the chrom/chromStart/chromEnd entries (the first three bigBed fields) of bedRow * with the lifted, target-assembly coordinates from liftedBed. Under quickLift bedRow is * loaded from the source-assembly interval, so without this a $chrom/${chromStart}/${chromEnd} * mouseOver substitution would report the pre-lift position instead of where the item is drawn. */ { safef(startBuf, bufSize, "%u", liftedBed->chromStart); safef(endBuf, bufSize, "%u", liftedBed->chromEnd); bedRow[0] = liftedBed->chrom; bedRow[1] = startBuf; bedRow[2] = endBuf; } void bigBedAddLinkedFeaturesFromExt(struct track *track, char *chrom, int start, int end, int scoreMin, int scoreMax, boolean useItemRgb, int fieldCount, struct linkedFeatures **pLfList, int maxItems) /* Read in items in chrom:start-end from bigBed file named in track->bbiFileName, convert * them to linkedFeatures, and add to head of list. */ { struct lm *lm = lmInit(0); struct trackDb *tdb = track->tdb; char *mouseOverField = cartOrTdbString(cart, track->tdb, "mouseOverField", NULL); // check if this track can merge large items, this setting must be allowed in the trackDb // stanza for the track, but can be enabled/disabled via trackUi/right click menu so // we also need to check the cart for the current status int mergeCount = 0; boolean doWindowSizeFilter = trackDbSettingOn(track->tdb, MERGESPAN_TDB_SETTING); if (doWindowSizeFilter) { char hasMergedItemsSetting[256]; safef(hasMergedItemsSetting, sizeof(hasMergedItemsSetting), "%s.%s", track->track, MERGESPAN_CART_SETTING); if (cartVarExists(cart, hasMergedItemsSetting)) doWindowSizeFilter = cartInt(cart, hasMergedItemsSetting) == 1; else // save the cart var so javascript can offer the right toggle cartSetInt(cart, hasMergedItemsSetting, 1); } /* protect against temporary network error */ struct bbiFile *bbi = NULL; struct errCatch *errCatch = errCatchNew(); if (errCatchStart(errCatch)) { bbi = fetchBbiForTrack(track); } errCatchEnd(errCatch); if (errCatch->gotError) { track->networkErrMsg = cloneString(errCatch->message->string); track->drawItems = bigDrawWarning; track->totalHeight = bigWarnTotalHeight; return; } errCatchFree(&errCatch); fieldCount = track->bedSize; if (fieldCount == 0) track->bedSize = fieldCount = bbi->definedFieldCount; +else if (fieldCount > bbi->fieldCount) + /* The type line asks for more fields than the file holds, and bedRow below is sized by + * the file's own count. Use that count: it is the only one the file can answer for, + * and it is the one hubCheck already requires the type line to match. */ + track->bedSize = fieldCount = bbi->fieldCount; struct bigBedInterval *bb, *bbList; char *quickLiftFile = cloneString(trackDbSetting(track->tdb, "quickLiftUrl")); struct hash *chainHash = NULL; if (quickLiftFile) bbList = quickLiftGetIntervals(quickLiftFile, bbi, chromName, winStart, winEnd, &chainHash); else bbList = bigBedSelectRangeExt(track, chrom, start, end, lm, maxItems); char *squishField = cartOrTdbString(cart, track->tdb, "squishyPackField", NULL); int squishFieldIdx = bbExtraFieldIndex(bbi, squishField); /* colorFields: optional alternative color scheme stored in a named extra field. */ int colorFieldIdx = 0; char *colorFieldsSetting = trackDbSettingClosestToHome(tdb, "colorFields"); if (useItemRgb && colorFieldsSetting) { char *colorFieldName = cartOptionalStringClosestToHome(cart, tdb, FALSE, "colorField"); if (!isEmpty(colorFieldName)) { colorFieldIdx = bbExtraFieldIndex(bbi, colorFieldName); /* Append "(Coloring by: