540506f7475055ae33969ce12d53a649ffff140e braney Mon Apr 28 16:55:59 2025 -0700 change BAM support to use maxItems as a signal to go into coverage mode. Added BAMMaxItems to limit the total number of items that might be retrieved from a BAM file. diff --git src/hg/hgTracks/bamTrack.c src/hg/hgTracks/bamTrack.c index fbfc138f815..01e718ba0e1 100644 --- src/hg/hgTracks/bamTrack.c +++ src/hg/hgTracks/bamTrack.c @@ -12,46 +12,66 @@ #include "htmshell.h" #include "hui.h" #include "jksql.h" #include "hdb.h" #include "hgTracks.h" #include "cds.h" #include "hgBam.h" #include "wigCommon.h" #include "knetUdc.h" #include "udc.h" #include "bigWarn.h" #include "errCatch.h" #include "hgConfig.h" #include "chromAlias.h" +#define TOOMANYITEMSERROR "Maximum number of BAM items exceeded. Perhaps zooming in a bit will help?" struct bamTrackData { struct track *tg; struct hash *pairHash; int minAliQual; char *colorMode; char *grayMode; char *userTag; int aliQualShadeMin; int aliQualShadeMax; int baseQualShadeMin; int baseQualShadeMax; + int count; + int maxItems; }; +static unsigned BAMMaxItems() +/* Get the maximum number of items to grab from a BAM file. Defaults to ten thousand . */ +{ +static boolean set = FALSE; +static unsigned maxItems = 0; + +if (!set) + { + char *maxItemsStr = cfgOptionDefault("BAMMaxItems", "10000"); + + maxItems = sqlUnsigned(maxItemsStr); + set = TRUE; + } + +return maxItems; +} + static struct psl *pslFromBam(const bam1_t *bam) /* Translate BAM's numeric CIGAR encoding into PSL sufficient for cds.c (just coords, * no scoring info) */ { const bam1_core_t *core = &bam->core; struct psl *psl; AllocVar(psl); boolean isRc = (core->flag & BAM_FREVERSE); psl->strand[0] = isRc ? '-' : '+'; psl->qName = cloneString(bam1_qname(bam)); psl->tName = cloneString(chromName); unsigned blockCount = 0; unsigned *blockSizes, *qStarts, *tStarts; AllocArray(blockSizes, core->n_cigar); AllocArray(qStarts, core->n_cigar); @@ -291,42 +311,45 @@ boolean passesFilters(const bam1_t *bam, struct bamTrackData *btd) /* Return TRUE if bam passes hgTrackUi-set filters. */ { if (bam == NULL) return FALSE; const bam1_core_t *core = &bam->core; // Always reject unmapped items -- nowhere to draw them. if (core->flag & BAM_FUNMAP) return FALSE; if (core->qual < btd->minAliQual) return FALSE; return TRUE; } -int addBam(const bam1_t *bam, void *data, bam_hdr_t *hdr) +static int addBam(const bam1_t *bam, void *data, bam_hdr_t *hdr) /* bam_fetch() calls this on each bam alignment retrieved. Translate each bam * into a linkedFeatures item, and add it to tg->items. */ { struct bamTrackData *btd = (struct bamTrackData *)data; if (!passesFilters(bam, btd)) return 0; struct linkedFeatures *lf = bamToLf(bam, data); if (lf) { struct track *tg = btd->tg; slAddHead(&(tg->items), lf); + btd->count++; + if (btd->count > btd->maxItems) + errAbort(TOOMANYITEMSERROR); } return 0; } static struct linkedFeatures *lfStub(int startEnd, int orientation) /* Make a linkedFeatures for a zero-length item (so that an arrow will be drawn * toward the given coord. */ { struct linkedFeatures *lf; AllocVar(lf); lf->name = cloneString("stub"); lf->orientation = orientation; struct simpleFeature *sf; AllocVar(sf); sf->start = sf->end = lf->start = lf->end = lf->tallStart = lf->tallEnd = startEnd; @@ -340,31 +363,31 @@ /* Make a linkedFeaturesSeries from one or two linkedFeatures elements. */ { struct linkedFeaturesSeries *lfs; AllocVar(lfs); lfs->name = cloneString(lf->name); lfs->grayIx = lf->grayIx; if (lf->next != NULL) slSort(&lf, linkedFeaturesCmpStart); lfs->orientation = 0; lfs->start = lf->start; lfs->end = lf->next ? max(lf->next->end, lf->end) : lf->end; lfs->features = lf; return lfs; } -int addBamPaired(const bam1_t *bam, void *data, bam_hdr_t *header) +static int addBamPaired(const bam1_t *bam, void *data, bam_hdr_t *header) /* bam_fetch() calls this on each bam alignment retrieved. Translate each bam * into a linkedFeaturesSeries item, and either store it until we find its mate * or add it to tg->items. */ { const bam1_core_t *core = &bam->core; struct bamTrackData *btd = (struct bamTrackData *)data; if (! passesFilters(bam, btd)) return 0; struct linkedFeatures *lf = bamToLf(bam, data); struct track *tg = btd->tg; if (!(core->flag & BAM_FPAIRED) || (core->flag & BAM_FMUNMAP)) { if (lf->start < winEnd && lf->end > winStart) slAddHead(&(tg->items), lfsFromLf(lf)); if ((core->flag & BAM_FMUNMAP) && sameString(btd->colorMode, BAM_COLOR_MODE_GRAY) && @@ -403,30 +426,33 @@ } else if (sameString(btd->colorMode, BAM_COLOR_MODE_GRAY) && sameString(btd->grayMode, BAM_GRAY_MODE_UNPAIRED)) // not properly paired: make it a lighter shade. lf->grayIx -= 4; hashAdd(btd->pairHash, lf->name, lf); } else { lfMate->next = lf; if (min(lfMate->start, lf->start) < winEnd && max(lfMate->end, lf->end) > winStart) slAddHead(&(tg->items), lfsFromLf(lfMate)); hashRemove(btd->pairHash, lf->name); } } +btd->count++; +if (btd->count > btd->maxItems) + errAbort(TOOMANYITEMSERROR); return 0; } #define MAX_ITEMS_FOR_MAPBOX 1500 static void dontMapItem(struct track *tg, struct hvGfx *hvg, void *item, char *itemName, char *mapItemName, int start, int end, int x, int y, int width, int height) /* When there are many many items, drawing hgc links can really slow us down. */ { } static int linkedFeaturesCmpOri(const void *va, const void *vb) /* Help sort linkedFeatures by strand, then by starting pos. */ { const struct linkedFeatures *a = *((struct linkedFeatures **)va); @@ -480,31 +506,31 @@ * that the data would just end up in dense mode and be super-slow. */ { /* protect against temporary network error */ struct errCatch *errCatch = errCatchNew(); if (errCatchStart(errCatch)) { struct hash *pairHash = isPaired ? hashNew(18) : NULL; int minAliQual = atoi(cartOrTdbString(cart, tg->tdb, BAM_MIN_ALI_QUAL, BAM_MIN_ALI_QUAL_DEFAULT)); char *colorMode = cartOrTdbString(cart, tg->tdb, BAM_COLOR_MODE, BAM_COLOR_MODE_DEFAULT); char *grayMode = cartOrTdbString(cart, tg->tdb, BAM_GRAY_MODE, BAM_GRAY_MODE_DEFAULT); char *userTag = cartOrTdbString(cart, tg->tdb, BAM_COLOR_TAG, BAM_COLOR_TAG_DEFAULT); int aliQualShadeMin = 0, aliQualShadeMax = 99, baseQualShadeMin = 0, baseQualShadeMax = 40; parseIntRangeSetting(tg->tdb, "aliQualRange", &aliQualShadeMin, &aliQualShadeMax); parseIntRangeSetting(tg->tdb, "baseQualRange", &baseQualShadeMin, &baseQualShadeMax); struct bamTrackData btd = {tg, pairHash, minAliQual, colorMode, grayMode, userTag, - aliQualShadeMin, aliQualShadeMax, baseQualShadeMin, baseQualShadeMax}; + aliQualShadeMin, aliQualShadeMax, baseQualShadeMin, baseQualShadeMax, 0, BAMMaxItems()}; char *fileName = trackDbSetting(tg->tdb, "bigDataUrl"); if (fileName == NULL) { if (tg->customPt) { errAbort("bamLoadItemsCore: can't find bigDataUrl for custom track %s", tg->track); } else { struct sqlConnection *conn = hAllocConnTrack(database, tg->tdb); fileName = bamFileNameFromTable(conn, tg->table, chromName); hFreeConn(&conn); } } @@ -559,31 +585,32 @@ sameString(grayMode, BAM_GRAY_MODE_ALI_QUAL)) slSort(&(tg->items), linkedFeaturesCmpScore); else slSort(&(tg->items), linkedFeaturesCmpStart); if (slCount(tg->items) > MAX_ITEMS_FOR_MAPBOX) { // flag drawItems to make a mapBox for the whole track tg->customInt = 1; tg->mapItem = dontMapItem; } } } errCatchEnd(errCatch); if (errCatch->gotError) { - tg->networkErrMsg = cloneString(errCatch->message->string); + tg->items = NULL; + tg->networkErrMsg = cloneString("Maximum number of BAM items exceeded. Perhaps zooming in a bit will help?"); tg->drawItems = bigDrawWarning; tg->totalHeight = bigWarnTotalHeight; } errCatchFree(&errCatch); } void bamLoadItems(struct track *tg) /* Load single-ended-only BAM data into tg->items item list, unless zoomed out so far * that the data would just end up in dense mode and be super-slow. */ { bamLoadItemsCore(tg, FALSE); }