5dc1d6e658ab009f27314e192340275a6bb70237 max Tue Jun 2 16:20:48 2026 -0700 Add colorFields trackDb setting for bigBed/bigGenePred color scheme switching Adds a new trackDb statement `colorFields` that renders a "Color by:" dropdown in the track controls page, letting users switch among multiple pre-computed color schemes stored as extra bigBed fields containing R,G,B strings. The `default="label"` key renames the standard itemRgb option in the dropdown. Other entries name extra bigBed fields whose R,G,B values override itemRgb when selected. When a non-default scheme is active, a "(Coloring by: label)" suffix appears in the track long label. Changes: - hui.c/hui.h: new colorFieldsCfgUi() rendered inside bedScoreCfgUi() for bigBed - bigBedTrack.c: colorFieldIdx lookup + per-item filterColor override + longLabel suffix - tagTypes.tab: register colorFields for bigBed and bigGenePred - trackDbLibrary.shtml, trackDbDoc.html, trackDbHub.v3.html, changes.html: documentation refs #26253 Co-Authored-By: Claude Sonnet 4.6 diff --git src/hg/hgTracks/bigBedTrack.c src/hg/hgTracks/bigBedTrack.c index 95edcc0f7b5..d48d1f4519b 100644 --- src/hg/hgTracks/bigBedTrack.c +++ src/hg/hgTracks/bigBedTrack.c @@ -1,1129 +1,1157 @@ /* 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 "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; } } } 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; boolean bigBedOnePath = cfgOptionBooleanDefault("bigBedOnePath", TRUE); if (bigBedOnePath && (fieldCount == 0)) track->bedSize = fieldCount = bbi->definedFieldCount; 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: