9f5a3871b95f595b5a218f0d05395a8c72d58844 braney Tue Aug 25 12:57:10 2026 -0700 remove the bigBedOnePath fallback, leaving a single bigBed load path, refs #36940 bigBedOnePath has defaulted to on since v492 and the setting has been removed from the hg.conf of the RR, Euro, Asia and hgwbeta. Drop the check and the code it guarded at all four sites: the field-count default in bigBedAddLinkedFeaturesFromExt, the method setup in commonBigBedMethods, the bedSize default in bigBedClick, and the bigBed case in hgc. The two branches in the hgc case differed only in a minimum the surviving path does not want. complexBedMethods lost its only isBigBed=TRUE caller with that branch, so drop the parameter. Retire the setting from the hg.conf catalog and its gate backlog, and repair the two hgc.c citations whose line numbers this commit shifted, refs #37925. Verified pixel-identical: 14 scenarios rendered before and after, covering the four QA sessions on the ticket plus mm10 knownGene, dbSnp155, rmsk, encRegTfbsClustered, clinvar and tandemDups. All AE=0. diff --git src/hg/hgTracks/bedTrack.c src/hg/hgTracks/bedTrack.c index cbb3c1c8be1..907215bbe48 100644 --- src/hg/hgTracks/bedTrack.c +++ src/hg/hgTracks/bedTrack.c @@ -1,1116 +1,1113 @@ /* bedTrack - handle most aspects of a bed-format track (or bigBed). */ /* Copyright (C) 2013 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include "common.h" #include "jksql.h" #include "bed.h" #include "hdb.h" #include "bedCart.h" #include "bbiFile.h" #include "bigBed.h" #include "hgTracks.h" #include "cds.h" #include "bedTabix.h" #include "obscure.h" #include "bigBedFilter.h" #include "bigBedLabel.h" #include "snake.h" #include "liftOver.h" #include "quickLift.h" #include "htmlColor.h" #define SEQ_DELIM '~' struct bed *bedLoadPairedTagAlign(char **row) /* Load first six fields of bed. * Add ~seq1~seq2 to end of name * Then remove the sequence to extra field when we convert to linkedFeature. * Assumes seq1 and seq2 are in row[6] and row[7], as they would be with a * pairedTagAlign type (hg/lib/encode/pairedTagAlign.as). It would be good to be * able to check these columns exist but we dont have the sqlResult here. */ { char buf[1024]; struct bed *ret = bedLoad6(row); safef(buf, sizeof(buf), "%s%c%s%c%s", ret->name, SEQ_DELIM, row[6], SEQ_DELIM, row[7]); freez(&(ret->name)); ret->name = cloneString(buf); return ret; } char *bedName(struct track *tg, void *item); void loadSimpleBedWithLoader(struct track *tg, bedItemLoader loader) /* Load the items in one track using specified loader - just move beds in window... */ { struct bed *bed, *list = NULL; char **row; int rowOffset; char *words[3]; int wordCt; char query[128]; bool doScoreCtFilter = FALSE; int scoreFilterCt = 0; char *setting; char *topTable = NULL; /* limit to a specified count of top scoring items. * If this is selected, it overrides selecting item by specified score */ if ((setting = trackDbSettingClosestToHome(tg->tdb, "filterTopScorers")) != NULL) { wordCt = chopLine(cloneString(setting), words); if (wordCt >= 3) { doScoreCtFilter = cartUsualBooleanClosestToHome(cart, tg->tdb, FALSE, "filterTopScorersOn",sameString(words[0], "on")); scoreFilterCt = cartUsualIntClosestToHome(cart, tg->tdb, FALSE, "filterTopScorersCt", atoi(words[1])); topTable = words[2]; /* if there are not too many rows in the table then can define */ /* top table as the track or subtrack table */ if (sameWord(topTable, "self")) topTable = cloneString(tg->table); } } /* Get list of items */ if (startsWith("bedTabix", tg->tdb->type ) || startsWith("longTabix", tg->tdb->type )) { struct hash *settings = tg->tdb->settingsHash; char *bigDataUrl = hashFindVal(settings, "bigDataUrl"); struct bedTabixFile *btf = bedTabixFileMayOpen(bigDataUrl, NULL, 0, 0); list = bedTabixReadBeds(btf, chromName, winStart, winEnd, loader); bedTabixFileClose(&btf); } else if (tg->isBigBed) { // avoid opening an unneeded db connection for bigBed; required not to use mysql for parallel fetch tracks struct lm *lm = lmInit(0); struct bbiFile *bbi = fetchBbiForTrack(tg); char *quickLiftFile = cloneString(trackDbSetting(tg->tdb, "quickLiftUrl")); struct hash *chainHash = NULL; struct bigBedInterval *bb, *bbList = NULL; if (quickLiftFile) bbList = quickLiftGetIntervals(quickLiftFile, bbi, chromName, winStart, winEnd, &chainHash); else bbList = bigBedSelectRange(tg, chromName, winStart, winEnd, lm); char *bedRow[bbi->fieldCount]; char startBuf[16], endBuf[16]; struct bigBedFilter *filters = bigBedBuildFilters(cart, bbi, tg->tdb); if (compositeChildHideEmptySubtracks(cart, tg->tdb, NULL, NULL)) labelTrackAsHideEmpty(tg); if (tg->itemName == bedName && !trackDbSettingClosestToHomeOn(tg->tdb, "linkIdInName")) tg->itemName = bigBedItemName; bigBedLabelCalculateFields(cart, tg->tdb, bbi, &tg->labelColumns); unsigned filtered = 0; for (bb = bbList; bb != NULL; bb = bb->next) { bigBedIntervalToRow(bb, chromName, startBuf, endBuf, bedRow, ArraySize(bedRow)); if (!bigBedFilterInterval(bbi, bedRow, filters)) { filtered++; continue; } if (quickLiftFile) { if ((bed = quickLiftIntervalsToBed(bbi, chainHash, bb)) == NULL) continue; } else bed = loader(bedRow); // FIXME BRANEY: either disable for all tracks with NUM_FIELDS > label field or better, // fix how label is stored so it doesn't trash custom bed field // BRANEY says: the loader should be returning bed structures which include the label field. if (differentString(tg->tdb->type, "bigInteract")) bed->label = bigBedMakeLabel(tg->tdb, tg->labelColumns, bb, chromName); slAddHead(&list, bed); } if (filtered) labelTrackAsFilteredNumber(tg, filtered); lmCleanup(&lm); } else { char *table = tg->table; struct customTrack *ct = tg->customPt; struct sqlConnection *conn = NULL; char *liftDb = cloneString(trackDbSetting(tg->tdb, "quickLiftDb")); char *scoreFilterClause = getScoreFilterClause(cart, tg->tdb,NULL); if (liftDb != NULL) { list = quickLiftSqlLoadBeds(tg->tdb, tg->table, liftDb, chromName, winStart, winEnd, scoreFilterClause, (ItemLoader2)loader, 0, FALSE); } else { if (ct == NULL) conn = hAllocConnTrack(database, tg->tdb); else { conn = hAllocConn(CUSTOM_TRASH); table = ct->dbTableName; } struct sqlResult *sr = NULL; /* limit to items above a specified score */ if (doScoreCtFilter && (topTable != NULL) && hTableExists(database, topTable)) { sqlSafef(query, sizeof(query),"select * from %s order by score desc limit %d", topTable, scoreFilterCt); sr = sqlGetResult(conn, query); rowOffset = hOffsetPastBin(database, hDefaultChrom(database), topTable); } else if(scoreFilterClause != NULL && tg->bedSize >= 5) { sr = hRangeQuery(conn, table, chromName, winStart, winEnd, scoreFilterClause, &rowOffset); } else { sr = hRangeQuery(conn, table, chromName, winStart, winEnd, NULL, &rowOffset); } freeMem(scoreFilterClause); while ((row = sqlNextRow(sr)) != NULL) { bed = loader(row+rowOffset); slAddHead(&list, bed); } sqlFreeResult(&sr); hFreeConn(&conn); } } if (doScoreCtFilter) { /* filter out items not in this window */ struct bed *newList = bedFilterListInRange(list, NULL, chromName, winStart, winEnd); list = newList; } slReverse(&list); tg->items = list; } void loadSimpleBed(struct track *tg) /* Load the items in one track - just move beds in * window... */ { struct bed *(*loader)(char **row); if (tg->bedSize <= 3) loader = bedLoad3; else if (tg->bedSize == 4) loader = bedLoad; else if (tg->bedSize == 5) loader = bedLoad5; else loader = bedLoad6; char *setting = NULL; // pairedTagAlign loader is required for base coloring using sequence from seq1 & seq2 // after removing optional bin column, this loader assumes seq1 and seq2 are in // row[6] and row[7] respectively of the sql result. if ((setting = trackDbSetting(tg->tdb, BASE_COLOR_USE_SEQUENCE)) && sameString(setting, "seq1Seq2")) loader = bedLoadPairedTagAlign; loadSimpleBedWithLoader(tg, loader); } void bed8To12(struct bed *bed) /* Turn a bed 8 into a bed 12 by defining one block. */ { // Make up a block: the whole thing. bed->blockCount = 1; bed->blockSizes = needMem(bed->blockCount * sizeof(int)); bed->chromStarts = needMem(bed->blockCount * sizeof(int)); bed->blockSizes[0] = bed->chromEnd - bed->chromStart; bed->chromStarts[0] = 0; // Some tracks overload thickStart and thickEnd -- catch garbage here. if ((bed->thickStart != 0) && ((bed->thickStart < bed->chromStart) || (bed->thickStart > bed->chromEnd))) bed->thickStart = bed->chromStart; if ((bed->thickEnd != 0) && ((bed->thickEnd < bed->chromStart) || (bed->thickEnd > bed->chromEnd))) bed->thickEnd = bed->chromEnd; } struct linkedFeatures *bedMungToLinkedFeatures(struct bed **pBed, struct trackDb *tdb, int fieldCount, int scoreMin, int scoreMax, boolean useItemRgb) /* Convert bed to a linkedFeature, destroying bed in the process. */ { struct bed *bed = *pBed; if (fieldCount < 12) bed8To12(bed); adjustBedScoreGrayLevel(tdb, bed, scoreMin, scoreMax); struct linkedFeatures *lf = lfFromBedExtra(bed, scoreMin, scoreMax); if (useItemRgb) { lf->useItemRgb = TRUE; lf->filterColor=bed->itemRgb; } bedFree(pBed); return lf; } void loadBed9(struct track *tg) /* Convert bed 9 info in window to linked feature. (to handle itemRgb)*/ { struct trackDb *tdb = tg->tdb; struct bed *bed; struct linkedFeatures *lfList = NULL, *lf; int scoreMin = atoi(trackDbSettingClosestToHomeOrDefault(tdb, "scoreMin", "0")); int scoreMax = atoi(trackDbSettingClosestToHomeOrDefault(tdb, "scoreMax", "1000")); boolean useItemRgb = FALSE; useItemRgb = bedItemRgb(tdb); if (tg->isBigBed) { // avoid opening an unneeded db connection for bigBed; required not to use mysql for parallel fetch tracks struct bbiFile *bbi = fetchBbiForTrack(tg); if (bbi->fieldCount < 9) errAbort("track %s has a bigBed being read as a bed9 that has %d columns.", tg->track, bbi->fieldCount); bigBedLabelCalculateFields(cart, tg->tdb, bbi, &tg->labelColumns); bigBedAddLinkedFeaturesFrom(tg, chromName, winStart, winEnd, scoreMin, scoreMax, useItemRgb, 9, &lfList); } else { char *liftDb = cloneString(trackDbSetting(tg->tdb, "quickLiftDb")); char *scoreFilterClause = getScoreFilterClause(cart, tg->tdb,NULL); if (liftDb == NULL) { struct sqlConnection *conn = hAllocConnTrack(database, tdb); struct sqlResult *sr; char **row; int rowOffset; char *scoreFilterClause = getScoreFilterClause(cart, tg->tdb,NULL); if (scoreFilterClause != NULL) { sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd,scoreFilterClause, &rowOffset); freeMem(scoreFilterClause); } else { sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd, NULL, &rowOffset); } while ((row = sqlNextRow(sr)) != NULL) { bed = bedLoadN(row+rowOffset, 9); lf = bedMungToLinkedFeatures(&bed, tdb, 9, scoreMin, scoreMax, useItemRgb); slAddHead(&lfList, lf); } sqlFreeResult(&sr); hFreeConn(&conn); } else { struct bed *liftedBeds = quickLiftSqlLoadBeds(tg->tdb, tg->table, liftDb, chromName, winStart, winEnd, scoreFilterClause, (ItemLoader2)bedLoadN, 9, FALSE); for(bed = liftedBeds; bed; bed = bed->next) { lf = lfFromBedExtra(bed, scoreMin, scoreMax); if (useItemRgb) { lf->useItemRgb = TRUE; lf->filterColor=bed->itemRgb; } slAddHead(&lfList, lf); } } } slReverse(&lfList); slSort(&lfList, linkedFeaturesCmp); tg->items = lfList; } void loadBed8(struct track *tg) /* Convert bed 8 info in window to linked feature. */ { struct bed *bed; struct linkedFeatures *lfList = NULL, *lf; struct trackDb *tdb = tg->tdb; int scoreMin = atoi(trackDbSettingClosestToHomeOrDefault(tdb, "scoreMin", "0")); int scoreMax = atoi(trackDbSettingClosestToHomeOrDefault(tdb, "scoreMax", "1000")); boolean useItemRgb = FALSE; useItemRgb = bedItemRgb(tdb); if (tg->isBigBed) { // avoid opening an unneeded db connection for bigBed; required not to use mysql for parallel fetch tracks struct bbiFile *bbi = fetchBbiForTrack(tg); bigBedLabelCalculateFields(cart, tg->tdb, bbi, &tg->labelColumns); bigBedAddLinkedFeaturesFrom(tg, chromName, winStart, winEnd, scoreMin, scoreMax, useItemRgb, 8, &lfList); } else { char *liftDb = cloneString(trackDbSetting(tg->tdb, "quickLiftDb")); char *scoreFilterClause = getScoreFilterClause(cart, tg->tdb,NULL); if (liftDb == NULL) { struct sqlConnection *conn = hAllocConn(database); struct sqlResult *sr; char **row; int rowOffset; if (scoreFilterClause != NULL) { sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd,scoreFilterClause, &rowOffset); freeMem(scoreFilterClause); } else { sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd, NULL, &rowOffset); } while ((row = sqlNextRow(sr)) != NULL) { bed = bedLoadN(row+rowOffset, 8); lf = bedMungToLinkedFeatures(&bed, tdb, 8, scoreMin, scoreMax, useItemRgb); slAddHead(&lfList, lf); } sqlFreeResult(&sr); hFreeConn(&conn); } else { struct bed *liftedBeds = quickLiftSqlLoadBeds(tg->tdb, tg->table, liftDb, chromName, winStart, winEnd, scoreFilterClause, (ItemLoader2)bedLoadN, 8, TRUE); for(bed = liftedBeds; bed; bed = bed->next) { lf = lfFromBedExtra(bed, scoreMin, scoreMax); if (useItemRgb) { lf->useItemRgb = TRUE; lf->filterColor=bed->itemRgb; } slAddHead(&lfList, lf); } } } slReverse(&lfList); slSort(&lfList, linkedFeaturesCmp); tg->items = lfList; } static void filterBed(struct track *tg, struct linkedFeatures **pLfList) /* Apply filters if any to mRNA linked features. */ { struct linkedFeatures *lf, *next, *newList = NULL, *oldList = NULL; struct mrnaUiData *mud = tg->extraUiData; struct mrnaFilter *fil; char *type; boolean anyFilter = FALSE; boolean colorIx = 0; boolean isExclude = FALSE; boolean andLogic = TRUE; if (*pLfList == NULL || mud == NULL) return; /* First make a quick pass through to see if we actually have * to do the filter. */ for (fil = mud->filterList; fil != NULL; fil = fil->next) { fil->pattern = cartUsualStringClosestToHome(cart, tg->tdb, FALSE, fil->suffix, ""); if (fil->pattern[0] != 0) anyFilter = TRUE; } if (!anyFilter) return; type = cartUsualStringClosestToHome(cart, tg->tdb, FALSE, mud->filterTypeSuffix, "red"); if (sameString(type, "exclude")) isExclude = TRUE; else if (sameString(type, "include")) isExclude = FALSE; else colorIx = getFilterColor(type, MG_BLACK); type = cartUsualStringClosestToHome(cart, tg->tdb, FALSE, mud->logicTypeSuffix, "and"); andLogic = sameString(type, "and"); /* Make a pass though each filter, and start setting up search for * those that have some text. */ for (fil = mud->filterList; fil != NULL; fil = fil->next) { if (fil->pattern[0] != 0) // Already retrieved above. fil->hash = newHash(10); } /* Scan tables id/name tables to build up hash of matching id's. */ for (fil = mud->filterList; fil != NULL; fil = fil->next) { struct hash *hash = fil->hash; int wordIx, wordCount; char *words[128]; if (hash != NULL) { boolean anyWild; char *dupPat = cloneString(fil->pattern); wordCount = chopLine(dupPat, words); for (wordIx=0; wordIx <wordCount; ++wordIx) { char *pattern = cloneString(words[wordIx]); if (lastChar(pattern) != '*') { int len = strlen(pattern)+1; pattern = needMoreMem(pattern, len, len+1); pattern[len-1] = '*'; } anyWild = (strchr(pattern, '*') != NULL || strchr(pattern, '?') != NULL); touppers(pattern); for(lf = *pLfList; lf != NULL; lf=lf->next) { char copy[SMALLBUF]; boolean gotMatch; safef(copy, sizeof(copy), "%s", lf->name); touppers(copy); if (anyWild) gotMatch = wildMatch(pattern, copy); else gotMatch = sameString(pattern, copy); if (gotMatch) { hashAdd(hash, lf->name, NULL); } } freez(&pattern); } freez(&dupPat); } } /* Scan through linked features coloring and or including/excluding ones that * match filter. */ for (lf = *pLfList; lf != NULL; lf = next) { boolean passed = andLogic; next = lf->next; for (fil = mud->filterList; fil != NULL; fil = fil->next) { if (fil->hash != NULL) { if (hashLookup(fil->hash, lf->name) == NULL) { if (andLogic) passed = FALSE; } else { if (!andLogic) passed = TRUE; } } } if (passed ^ isExclude) { slAddHead(&newList, lf); if (colorIx > 0) lf->filterColor = colorIx; } else { slAddHead(&oldList, lf); } } slReverse(&newList); slReverse(&oldList); if (colorIx > 0) { /* Draw stuff that passes filter first in full mode, last in dense. */ if (tg->visibility == tvDense) { newList = slCat(oldList, newList); } else { newList = slCat(newList, oldList); } } *pLfList = newList; tg->limitedVisSet = FALSE; /* Need to recalculate this after filtering. */ /* Free up hashes, etc. */ for (fil = mud->filterList; fil != NULL; fil = fil->next) { hashFree(&fil->hash); } } void loadGappedBed(struct track *tg) /* Convert bed info in window to linked feature. */ { struct sqlResult *sr; char **row; int rowOffset; struct bed *bed; struct linkedFeatures *lfList = NULL, *lf; struct trackDb *tdb = tg->tdb; int scoreMin = atoi(trackDbSettingClosestToHomeOrDefault(tdb, "scoreMin", "0")); int scoreMax = atoi(trackDbSettingClosestToHomeOrDefault(tdb, "scoreMax", "1000")); boolean useItemRgb = FALSE; useItemRgb = bedItemRgb(tdb); if (tg->isBigBed) { // avoid opening an unneeded db connection for bigBed; required not to use mysql for parallel fetch tracks struct bbiFile *bbi = fetchBbiForTrack(tg); bigBedLabelCalculateFields(cart, tg->tdb, bbi, &tg->labelColumns); bigBedAddLinkedFeaturesFrom(tg, chromName, winStart, winEnd, scoreMin, scoreMax, useItemRgb, 12, &lfList); } else { /* Use tg->tdb->track because subtracks inherit composite track's tdb * by default, and the variable is named after the composite track. */ char *liftDb = cloneString(trackDbSetting(tg->tdb, "quickLiftDb")); char *scoreFilterClause = getScoreFilterClause(cart, tg->tdb,NULL); if (liftDb == NULL) { struct sqlConnection *conn = hAllocConn(database); if (scoreFilterClause != NULL) { sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd,scoreFilterClause, &rowOffset); freeMem(scoreFilterClause); } else { sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd, NULL, &rowOffset); } while ((row = sqlNextRow(sr)) != NULL) { bed = bedLoad12(row+rowOffset); adjustBedScoreGrayLevel(tdb, bed, scoreMin, scoreMax); lf = lfFromBedExtra(bed, scoreMin, scoreMax); if (useItemRgb) { lf->useItemRgb = TRUE; lf->filterColor=bed->itemRgb; } slAddHead(&lfList, lf); bedFree(&bed); } sqlFreeResult(&sr); hFreeConn(&conn); } else { struct bed *liftedBeds = quickLiftSqlLoadBeds(tg->tdb, tg->table, liftDb, chromName, winStart, winEnd, scoreFilterClause, (ItemLoader2)bedLoad12, 0, TRUE); for(bed = liftedBeds; bed; bed = bed->next) { lf = lfFromBedExtra(bed, scoreMin, scoreMax); if (useItemRgb) { lf->useItemRgb = TRUE; lf->filterColor=bed->itemRgb; } slAddHead(&lfList, lf); } } } slReverse(&lfList); if(tg->extraUiData) filterBed(tg, &lfList); slSort(&lfList, linkedFeaturesCmp); tg->items = lfList; filterItemsOnNames(tg); maybeLoadSnake(tg); // if we're in snake mode, change the methods } Color colorFromCart(struct track *tg, Color color) /* Return the RGB color from the cart setting 'colorOverride' or just return color */ { if (!cartUsualBooleanClosestToHome(cart, tg->tdb, FALSE, "colorOverrideOn", FALSE)) return color; char *hexColorStr = cartOptionalStringClosestToHome(cart, tg->tdb, FALSE, "colorOverride"); if (isEmpty(hexColorStr)) return color; unsigned rgb; if (!htmlColorForCode(hexColorStr, &rgb)) return color; int r, g, b; htmlColorToRGB(rgb, &r, &g, &b); tg->itemColor = NULL; tg->itemNameColor = NULL; return MAKECOLOR_32(r, g, b); } void bedDrawSimpleAt(struct track *tg, void *item, struct hvGfx *hvg, int xOff, int y, double scale, MgFont *font, Color color, enum trackVisibility vis) /* Draw a single simple bed item at position. */ { struct bed *bed = item; int heightPer = tg->heightPer; int s = max(bed->chromStart, winStart), e = min(bed->chromEnd, winEnd); if (s > e) return; int x1 = round((s-winStart)*scale) + xOff; int x2 = round((e-winStart)*scale) + xOff; int w = x2 - x1; if (w < 1) w = 1; struct trackDb *tdb = tg->tdb; int scoreMin = atoi(trackDbSettingClosestToHomeOrDefault(tdb, "scoreMin", "0")); int scoreMax = atoi(trackDbSettingClosestToHomeOrDefault(tdb, "scoreMax", "1000")); char *directUrl = trackDbSetting(tdb, "directUrl"); boolean withHgsid = (trackDbSetting(tdb, "hgsid") != NULL); boolean thickDrawItem = (trackDbSetting(tdb, "thickDrawItem") != NULL); if (tg->itemColor != NULL) { color = tg->itemColor(tg, bed, hvg); } else if (tg->colorShades) { adjustBedScoreGrayLevel(tdb, bed, scoreMin, scoreMax); color = tg->colorShades[grayInRange(bed->score, scoreMin, scoreMax)]; } color = colorFromCart(tg, color); // user-chosen per-item color (right-click "Color this item"). Simple bed has no background-box // support, so both modes just recolor the glyph. Color itemColorOver; if (itemColorOverride(tg, bed, &itemColorOver, NULL)) color = itemColorOver; /* Keep the item at least 4 pixels wide at all viewpoints */ if (thickDrawItem && (w < 4)) { x1 -= ((5-w) >> 1); w = 4; x2 = x1 + w; } if (color) { hvGfxBox(hvg, x1, y, w, heightPer, color); if (tg->drawLabelInBox) { char *label = tg->itemName(tg, bed); drawScaledBoxLabel(hvg, s, e, scale, xOff, y, heightPer, color, font, label); } else if (tg->drawName && vis != tvSquish) { /* Clip here so that text will tend to be more visible... */ char *s = tg->itemName(tg, bed); w = x2-x1; if (w > mgFontStringWidth(font, s)) { Color textColor = hvGfxContrastingColor(hvg, color); hvGfxTextCentered(hvg, x1, y, w, heightPer, textColor, font, s); } mapBoxHgcOrHgGene(hvg, bed->chromStart, bed->chromEnd, x1, y, x2 - x1, heightPer, tg->track, tg->mapItemName(tg, bed), NULL, directUrl, withHgsid, NULL); } } if ((tg->subType == lfWithBarbs || tg->exonArrows) && !tg->drawLabelInBox) { int dir = 0; if (bed->strand[0] == '+') dir = 1; else if(bed->strand[0] == '-') dir = -1; if (dir != 0 && w > 2) { int midY = y + (heightPer>>1); Color textColor = hvGfxContrastingColor(hvg, color); clippedBarbs(hvg, x1, midY, w, tl.barbHeight, tl.barbSpacing, dir, textColor, TRUE); } } } void bedDrawSimple(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw simple Bed items. */ { if (!tg->drawItemAt) errAbort("missing drawItemAt in track %s", tg->track); color = colorFromCart(tg, color); if (tg->items == NULL && vis == tvDense && canDrawBigBedDense(tg)) { bigBedDrawDense(tg, seqStart, seqEnd, hvg, xOff, yOff, width, font, color); } else genericDrawItems(tg, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, vis); } char *bedName(struct track *tg, void *item) /* Return name of bed track item. */ { struct bed *bed = item; if (bed->name == NULL) return ""; return bed->name; } char *bedNameField1(struct track *tg, void *item) /* return part before first space in item name */ { struct bed *bed = item; return cloneFirstWord(bed->name); } char *bedNameNotField1(struct track *tg, void *item) /* return part after first space in item name */ { struct bed *bed = item; return cloneNotFirstWord(bed->name); } int bedItemStart(struct track *tg, void *item) /* Return start position of item. */ { struct bed *bed = item; return bed->chromStart; } int bedItemEnd(struct track *tg, void *item) /* Return end position of item. */ { struct bed *bed = item; return bed->chromEnd; } void freeSimpleBed(struct track *tg) /* Free the beds in a track group that has beds as its items. */ { bedFreeList(((struct bed **)(&tg->items))); } boolean simpleBedNextPrevEdge(struct track *tg, struct hvGfx *hvg, void *item, int x, int y, int w, int h, boolean next) /* Like linkedFeaturesNextPrevItem, but for simple bed which has no block structure so * this simply zaps us to the right/left edge of the feature. Arrows have already been * drawn; here we figure out coords and draw a mapBox. */ { boolean result = FALSE; struct bed4 *bed = item; char *mouseOverText = NULL; if (next) mouseOverText = trackDbSettingClosestToHomeOrDefault(tg->tdb, "nextExonText", revCmplDisp ? "Left Edge" : "Right Edge"); else mouseOverText = trackDbSettingClosestToHomeOrDefault(tg->tdb, "prevExonText", revCmplDisp ? "Right Edge" : "Left Edge"); int winSize = winEnd - winStart; int bufferToEdge = 0.05 * winSize; int newWinStart, newWinEnd; if (next) { newWinEnd = bed->chromEnd + bufferToEdge; newWinStart = newWinEnd - winSize; } else { newWinStart = bed->chromStart - bufferToEdge; newWinEnd = newWinStart + winSize; } struct convertRange *cr=NULL; AllocVar(cr); if (( next && newWinEnd > winStart) || (!next && newWinStart < winEnd )) { cr->chrom = bed->chrom; cr->start = newWinStart; cr->end = newWinEnd; } else { cr->skipIt = TRUE; } linkedFeaturesNextPrevExonFind(tg, next, cr); long newVirtWinStart = cr->vStart; long newVirtWinEnd = cr->vEnd; if (newVirtWinStart != -1) // found it { mapBoxJumpTo(hvg, x, y, w, h, tg, virtChromName, newVirtWinStart, newVirtWinEnd, mouseOverText); result = TRUE; } return result; } void bedMethods(struct track *tg) /* Fill in methods for (simple) bed tracks. */ { tg->drawItems = bedDrawSimple; tg->drawItemAt = bedDrawSimpleAt; tg->itemName = bedName; tg->mapItemName = bedName; tg->drawItemLabel = genericDrawItemLabel; tg->doItemMapAndArrows = genericItemMapAndArrows; tg->totalHeight = tgFixedTotalHeightNoOverflow; tg->itemHeight = tgFixedItemHeight; tg->itemStart = bedItemStart; tg->itemEnd = bedItemEnd; // Adding "tg->nextPrevExon = simpleBedNextPrevEdge;" opened a can of worms: too many // bed-based tracks have their own drawItems methods that don't hook into nextItem stuff, // or drawItemAt methods that draw mapboxes but tg->mapsSelf is not set... // So, set tg->nextPrevExon = simpleBedNextPrevEdge case-by-case. tg->nextPrevItem = linkedFeaturesLabelNextPrevItem; tg->freeItems = freeSimpleBed; if (trackDbSettingClosestToHomeOn(tg->tdb, "linkIdInName")) { tg->mapItemName = bedNameField1; tg->itemName = bedNameNotField1; } } void bed9Methods(struct track *tg) /* Fill in methods for bed9 tracks. */ { linkedFeaturesMethods(tg); tg->loadItems = loadBed9; } void addSimpleFeatures(struct simpleFeature **pSfList, int start, int end, int qStart, boolean everyBase) /* Add simple features from start to end to the list. * qStart is the offset in the query. * If everyBase is TRUE then add a simpleFeature for every base in the start,end region */ { int s; struct simpleFeature *sf; int stepSize = everyBase ? 1 : end - start; for (s = start ; s < end ; s += stepSize) { AllocVar(sf); sf->start = s; sf->end = sf->start + stepSize; sf->qStart = qStart + (s - start); sf->qEnd = sf->qStart + stepSize; slAddHead(pSfList, sf); } } struct linkedFeatures *simpleBedToLinkedFeatures(struct bed *b, int bedFields, boolean everyBase, boolean paired) /* Create a linked feature from a single bed item * Any bed fields past the 6th field (strand) will be ignored * Make one simpleFeature for every base of the bed if everyBase is TRUE, * otherwise it will contain a single 'exon' corresponding to the bed (start,end) * Dont free the bed as a pointer to each item is stored in lf->original * If paired then need to strip ~seq1~seq2 from name and set it as DNA in lf->extra * and treat bed as 2-exon bed. */ { struct linkedFeatures *lf = NULL; if (b) { AllocVar(lf); lf->start = lf->tallStart = b->chromStart; lf->end = lf->tallEnd = b->chromEnd; lf->components = NULL; if (bedFields > 5) // need to know orientation before checking if paired lf->orientation = (b->strand[0] == '+' ? 1 : (b->strand[0] == '-' ? -1 : 0)); if (paired) { // Find seq1 & seq2, strip them from the name, // add them as two blocks of simpleFeatures, // concatenate seq1 & seq2 into one dnaSeq, // and store in lf->extra field char *seq2 = strrchr(b->name, SEQ_DELIM); if (!seq2) errAbort("Could not find seq2 in paired sequence"); *(seq2++) = '\0'; // Find seq1 and strip it from the name char *seq1 = strrchr(b->name, SEQ_DELIM); if (!seq1) errAbort("Could not find seq1 in paired sequence"); *(seq1++) = '\0'; int l1 = strlen(seq1); int l2 = strlen(seq2); struct dyString *d = dyStringNew(l1+l2+1); dyStringAppend(d, seq1); dyStringAppend(d, seq2); lf->extra = newDnaSeq(dyStringCannibalize(&d), l1+l2, lf->name); // seq1 + seq2 are concatenated for drawing code // in - orientation they are also reverse complemented, // and the query start is relative to the reverse complement, so: // in + orientation first feature (f1) is seq1, second (f2) is seq2 // in - orientation first feature (f1) is seq2, second (f2) is seq1 int f1 = lf->orientation == -1 ? l2 : l1; int f2 = lf->orientation == -1 ? l1 : l2; addSimpleFeatures(&lf->components, lf->start, lf->start + f1, 0, everyBase); addSimpleFeatures(&lf->components, lf->end - f2, lf->end, f1, everyBase); } else { addSimpleFeatures(&lf->components, lf->start, lf->end, 0, everyBase); } slReverse(&lf->components); if (bedFields > 3) lf->name = cloneString(b->name); if (bedFields > 4) lf->score = b->score; lf->original = b; } return lf; } struct linkedFeatures *simpleBedListToLinkedFeatures(struct bed *b, int bedFields, boolean everyBase, boolean paired) /* Create a list of linked features from a list of beds */ { struct linkedFeatures *lfList = NULL; while (b) { slAddHead(&lfList, simpleBedToLinkedFeatures(b, bedFields, everyBase, paired)); b = b->next; } slReverse(&lfList); return lfList; } void loadSimpleBedAsLinkedFeaturesPerBase(struct track *tg) /* bed list not freed as pointer to it is stored in 'original' field */ { loadSimpleBed(tg); tg->items = simpleBedListToLinkedFeatures(tg->items, tg->bedSize, TRUE, FALSE); } void loadPairedTagAlignAsLinkedFeaturesPerBase(struct track *tg) /* bed list not freed as pointer to it is stored in 'original' field */ { loadSimpleBed(tg); tg->items = simpleBedListToLinkedFeatures(tg->items, tg->bedSize, TRUE, TRUE); } static Color itemColorByStrand(struct track *tg, int orientation, struct hvGfx *hvg) /* Look up the RGB color from the trackDb setting 'colorByStrand' based on * the orientation (1='+', 0=unknown, -1='-'), and convert this to a color index * using hvGfx */ { char *words[3]; unsigned char r, g, b; char *colors = cloneString(trackDbSetting(tg->tdb, "colorByStrand")); if (!colors) errAbort("colorByStrand setting missing (in %s)", tg->track); if (chopByWhite(colors, words, sizeof(words)) != 2) errAbort("invalid colorByStrand setting %s (expecting pair of RGB values r,g,b r,g,b)", colors); if (orientation == 1) parseColor(words[0], &r, &g, &b); else if (orientation == -1) parseColor(words[1], &r, &g, &b); else // return the main color { r = tg->color.r; g = tg->color.g; b = tg->color.b; } freez(&colors); return hvGfxFindColorIx(hvg, r, g, b); } Color lfItemColorByStrand(struct track *tg, void *item, struct hvGfx *hvg) /* Look up the RGB color from the trackDb setting 'colorByStrand' based on * the linkedFeature item orientation, and convert this to a color index * using hvGfx */ { struct linkedFeatures *lf = item; return itemColorByStrand(tg, lf->orientation, hvg); } Color bedItemColorByStrand(struct track *tg, void *item, struct hvGfx *hvg) /* Look up the RGB color from the trackDb setting 'colorByStrand' based on * the bed item strand, and convert this to a color index * using hvGfx */ { struct bed *b = item; return itemColorByStrand(tg, (b->strand[0] == '+' ? 1 : (b->strand[0] == '-' ? -1 : 0)), hvg); } -void complexBedMethods(struct track *track, struct trackDb *tdb, boolean isBigBed, +void complexBedMethods(struct track *track, struct trackDb *tdb, int wordCount, char *words[]) /* Fill in methods for more complex bed tracks. */ { int fieldCount = 3; boolean useItemRgb = FALSE; useItemRgb = bedItemRgb(tdb); if (wordCount > 1) fieldCount = atoi(words[1]); track->bedSize = fieldCount; -track->isBigBed = isBigBed; - -//if (track->isBigBed) - //track->nextItemButtonable = FALSE; +track->isBigBed = FALSE; if (fieldCount < 8) { if (baseColorGetDrawOpt(track) != baseColorDrawOff) { // data must be loaded as bed and converted to linkedFeatures // to draw each base character must make one simpleFeature per base linkedFeaturesMethods(track); char *setting = trackDbSetting(tdb, BASE_COLOR_USE_SEQUENCE); if (isNotEmpty(setting) && sameString(setting, "seq1Seq2")) track->loadItems = loadPairedTagAlignAsLinkedFeaturesPerBase; else track->loadItems = loadSimpleBedAsLinkedFeaturesPerBase; if (trackDbSetting(tdb, "colorByStrand")) { track->itemColor = lfItemColorByStrand; } } else { bedMethods(track); track->loadItems = loadSimpleBed; if (trackDbSetting(tdb, "colorByStrand")) { track->itemColor = bedItemColorByStrand; } } } else if (useItemRgb && fieldCount == 9) { linkedFeaturesMethods(track); track->loadItems = loadBed9; } else if (fieldCount < 12) { linkedFeaturesMethods(track); track->loadItems = loadBed8; if (trackDbSetting(tdb, "colorByStrand")) { track->itemColor = lfItemColorByStrand; } } else { linkedFeaturesMethods(track); track->extraUiData = newBedUiData(track->track); track->loadItems = loadGappedBed; if (trackDbSetting(tdb, "colorByStrand")) { track->itemColor = lfItemColorByStrand; } } }