e0b7a6700dfca47e0723d2d5a2e5b3c745f51315 jcasper Tue Feb 28 12:38:00 2023 -0800 Fixing a few missed cases for transparency, refs #30569 diff --git src/hg/hgTracks/chainTrack.c src/hg/hgTracks/chainTrack.c index eac2b0c..900bbbb 100644 --- src/hg/hgTracks/chainTrack.c +++ src/hg/hgTracks/chainTrack.c @@ -1,621 +1,623 @@ /* chainTrack - stuff to load and display chain type tracks in * browser. Chains are typically from cross-species genomic * alignments. */ /* 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 "hash.h" #include "localmem.h" #include "linefile.h" #include "jksql.h" #include "hdb.h" #include "hgTracks.h" #include "chainBlock.h" #include "chainLink.h" #include "chainDb.h" #include "chainCart.h" #include "hgColors.h" #include "hubConnect.h" #include "chromAlias.h" #include "hgConfig.h" #include "snake.h" struct cartOptions { enum chainColorEnum chainColor; /* ChromColors, ScoreColors, NoColors */ int scoreFilter ; /* filter chains by score if > 0 */ }; struct sqlClosure { struct sqlConnection *conn; }; struct bbClosure { struct bbiFile *bbi; }; typedef void (*linkRetrieveFunc)(void *closure, char *fullName, struct lm *lm, struct hash *hash, int start, int end, char * chainId, boolean isSplit); static void doBbQuery(void *closure, char *fullName, struct lm *lm, struct hash *hash, int start, int end, char * chainId, boolean isSplit) /* doBbQuery- check a bigBed for chain elements between * start and end. Use the passed hash to resolve chain * id's and place the elements into the right * linkedFeatures structure */ { struct bbClosure *bbClosure = (struct bbClosure *)closure; struct bbiFile *bbi = bbClosure->bbi; struct linkedFeatures *lf; struct simpleFeature *sf; struct bigBedInterval *bb, *bbList = bigBedIntervalQuery(bbi, chromName, start, end, 0, lm); char *bedRow[5]; char startBuf[16], endBuf[16]; for (bb = bbList; bb != NULL; bb = bb->next) { bigBedIntervalToRow(bb, chromName, startBuf, endBuf, bedRow, ArraySize(bedRow)); lf = hashFindVal(hash, bedRow[3]); if (lf != NULL) { lmAllocVar(lm, sf); sf->start = sqlUnsigned(bedRow[1]); sf->end = sqlUnsigned(bedRow[2]); sf->qStart = sqlUnsigned(bedRow[4]); sf->qEnd = sf->qStart + (sf->end - sf->start); slAddHead(&lf->components, sf); } } } static void doQuery(void *closure, char *fullName, struct lm *lm, struct hash *hash, int start, int end, char * chainId, boolean isSplit) /* doQuery- check the database for chain elements between * start and end. Use the passed hash to resolve chain * id's and place the elements into the right * linkedFeatures structure */ { struct sqlClosure *sqlClosure = (struct sqlClosure *)closure; struct sqlConnection *conn = sqlClosure->conn; struct sqlResult *sr = NULL; char **row; struct linkedFeatures *lf; struct simpleFeature *sf; struct dyString *query = dyStringNew(1024); if (chainId == NULL) { sqlDyStringPrintf(query, "select chainId,tStart,tEnd,qStart from %sLink ", fullName); if (isSplit) sqlDyStringPrintf(query,"force index (bin) "); sqlDyStringPrintf(query,"where "); } else sqlDyStringPrintf(query, "select chainId, tStart,tEnd,qStart from %sLink where chainId=%s and ", fullName, chainId); if (!isSplit) sqlDyStringPrintf(query, "tName='%s' and ", chromName); hAddBinToQuery(start, end, query); sqlDyStringPrintf(query, "tStart<%u and tEnd>%u", end, start); sr = sqlGetResult(conn, query->string); /* Loop through making up simple features and adding them * to the corresponding linkedFeature. */ while ((row = sqlNextRow(sr)) != NULL) { lf = hashFindVal(hash, row[0]); if (lf != NULL) { lmAllocVar(lm, sf); sf->start = sqlUnsigned(row[1]); sf->end = sqlUnsigned(row[2]); sf->qStart = sqlUnsigned(row[3]); sf->qEnd = sf->qStart + (sf->end - sf->start); slAddHead(&lf->components, sf); } } sqlFreeResult(&sr); dyStringFree(&query); } static void loadLinks(struct track *tg, int seqStart, int seqEnd, enum trackVisibility vis) { struct linkedFeatures *lf; struct simpleFeature *sf; struct lm *lm; struct hash *hash; /* Hash of chain ids. */ #ifdef OLD double scale = ((double)(winEnd - winStart))/width; #endif /* OLD */ char fullName[64]; int start, end, extra; struct simpleFeature *lastSf = NULL; int maxOverLeft = 0, maxOverRight = 0; int overLeft, overRight; void *closure; struct sqlClosure sqlClosure; struct bbClosure bbClosure; linkRetrieveFunc queryFunc; if (tg->isBigBed) { closure = &bbClosure; queryFunc = doBbQuery; char *fileName = trackDbSetting(tg->tdb, "linkDataUrl"); if (fileName == NULL) { warn("Cannot find linkDataUrl in custom track \"%s\"\n", tg->shortLabel); return; } struct bbiFile *bbi = bigBedFileOpenAlias(fileName, chromAliasFindAliases); if (bbi == NULL) return; bbClosure.bbi = bbi; } else { closure = &sqlClosure; queryFunc = doQuery; sqlClosure.conn = hAllocConn(database); } lm = lmInit(1024*4); hash = newHash(0); /* Make up a hash of all linked features keyed by * id, which is held in the extras field. To * avoid burning memory on full chromosome views * we'll just make a single simple feature and * exclude from hash chains less than three pixels wide, * since these would always appear solid. */ for (lf = tg->items; lf != NULL; lf = lf->next) { #ifdef OLD double pixelWidth = (lf->end - lf->start) / scale; if (pixelWidth >= 2.5) #endif /* OLD */ { hashAdd(hash, lf->extra, lf); overRight = lf->end - seqEnd; if (overRight > maxOverRight) maxOverRight = overRight; overLeft = seqStart - lf->start ; if (overLeft > maxOverLeft) maxOverLeft = overLeft; } #ifdef OLD else { lmAllocVar(lm, sf); sf->start = lf->start; sf->end = lf->end; sf->grayIx = lf->grayIx; lf->components = sf; } #endif /* OLD */ } /* if some chains are actually loaded */ if (hash->elCount) { boolean isSplit = TRUE; /* Make up range query. */ safef(fullName, sizeof fullName, "%s_%s", chromName, tg->table); if (isHubTrack(tg->table) || !hTableExists(database, fullName)) { strcpy(fullName, tg->table); isSplit = FALSE; } /* in dense mode we don't draw the lines * so we don't need items off the screen */ if (vis == tvDense) queryFunc(closure, fullName, lm, hash, seqStart, seqEnd, NULL, isSplit); else { /* if chains extend beyond edge of window we need to get * elements that are off the screen * in both directions so we know whether to draw * one or two lines to the edge of the screen. */ #define STARTSLOP 10000 #define MULTIPLIER 10 #define MAXLOOK 100000 extra = (STARTSLOP < maxOverLeft) ? STARTSLOP : maxOverLeft; start = seqStart - extra; extra = (STARTSLOP < maxOverRight) ? STARTSLOP : maxOverRight; end = seqEnd + extra; queryFunc(closure, fullName, lm, hash, start, end, NULL, isSplit); for (lf = tg->items; lf != NULL; lf = lf->next) { if (lf->components != NULL) slSort(&lf->components, linkedFeaturesCmpStart); extra = (STARTSLOP < maxOverRight)?STARTSLOP:maxOverRight; end = seqEnd + extra; for (lastSf=sf=lf->components; sf; lastSf=sf, sf=sf->next) ; while (lf->end > end ) { /* get out if we have an element off right side */ if (( (lastSf != NULL) &&(lastSf->end > seqEnd)) || (extra > MAXLOOK)) break; extra *= MULTIPLIER; start = end; end = start + extra; queryFunc(closure, fullName, lm, hash, start, end, lf->extra, isSplit); if (lf->components != NULL) slSort(&lf->components, linkedFeaturesCmpStart); for (sf=lastSf; sf != NULL; lastSf=sf, sf=sf->next) ; } /* if we didn't find an element off to the right , add one */ if ((lf->end > seqEnd) && ((lastSf == NULL) ||(lastSf->end < seqEnd))) { lmAllocVar(lm, sf); sf->start = seqEnd; sf->end = seqEnd+1; sf->grayIx = lf->grayIx; sf->qStart = 0; sf->qEnd = sf->qStart + (sf->end - sf->start); sf->next = lf->components; lf->components = sf; slSort(&lf->components, linkedFeaturesCmpStart); } /* we know we have a least one component off right * now look for one off left */ extra = (STARTSLOP < maxOverLeft) ? STARTSLOP:maxOverLeft; start = seqStart - extra; while((extra < MAXLOOK) && (lf->start < seqStart) && (lf->components != NULL) && (lf->components->start > seqStart)) { extra *= MULTIPLIER; end = start; start = end - extra; if (start < 0) start = 0; queryFunc(closure, fullName, lm, hash, start, end, lf->extra, isSplit); slSort(&lf->components, linkedFeaturesCmpStart); } if ((lf->components != NULL) && (lf->components->start > seqStart) && (lf->start < lf->components->start)) { lmAllocVar(lm, sf); sf->start = 0; sf->end = 1; sf->grayIx = lf->grayIx; sf->qStart = lf->components->qStart; sf->qEnd = sf->qStart + (sf->end - sf->start); sf->next = lf->components; lf->components = sf; slSort(&lf->components, linkedFeaturesCmpStart); } } } } for (lf = tg->items; lf != NULL; lf = lf->next) { if ((lf) && lf->orientation == -1) { struct simpleFeature *sf = lf->components; for(; sf; sf = sf->next) { int temp; temp = sf->qStart; sf->qStart = lf->qSize - sf->qEnd; sf->qEnd = lf->qSize - temp; } } } if (tg->isBigBed) bbiFileClose(&bbClosure.bbi); else hFreeConn(&sqlClosure.conn); } void chainDraw(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw chained features. This loads up the simple features from * the chainLink table, calls linkedFeaturesDraw, and then * frees the simple features again. */ { if (tg->items == NULL) /*Exit Early if nothing to do */ return; linkedFeaturesDraw(tg, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, vis); } void bigChainLoadItems(struct track *tg) /* Load up all of the chains from correct table into tg->items * item list. At this stage to conserve memory for other tracks * we don't load the links into the components list until draw time. */ { boolean doSnake = cartOrTdbBoolean(cart, tg->tdb, "doSnake" , FALSE); struct linkedFeatures *list = NULL, *lf; int qs; char *optionChrStr; struct cartOptions *chainCart; chainCart = (struct cartOptions *) tg->extraUiData; optionChrStr = cartOptionalStringClosestToHome(cart, tg->tdb, FALSE, "chromFilter"); struct bbiFile *bbi = fetchBbiForTrack(tg); struct lm *lm = lmInit(0); struct bigBedInterval *bb, *bbList = bigBedIntervalQuery(bbi, chromName, winStart, winEnd, 0, lm); int fieldCount = 11; char *bedRow[fieldCount]; char startBuf[16], endBuf[16]; for (bb = bbList; bb != NULL; bb = bb->next) { bigBedIntervalToRow(bb, chromName, startBuf, endBuf, bedRow, ArraySize(bedRow)); if ((optionChrStr != NULL) && !startsWith(optionChrStr, bedRow[7])) continue; if (chainCart->scoreFilter >0) { unsigned score = sqlUnsigned(bedRow[4]); if (score < chainCart->scoreFilter) continue; } struct bed *bed = bedLoadN(bedRow, 6); lf = bedMungToLinkedFeatures(&bed, tg->tdb, fieldCount, 0, 1000, FALSE); if (*bedRow[5] == '-') { lf->orientation = -1; qs = sqlUnsigned(bedRow[8]) - sqlUnsigned(bedRow[10]); } else { lf->orientation = 1; qs = sqlUnsigned(bedRow[9]); } int len = strlen(bedRow[7]) + 32; lf->name = needMem(len); if (!doSnake) safef(lf->name, len, "%s %c %dk", bedRow[7], *bedRow[5], qs/1000); else safef(lf->name, len, "%s", bedRow[7]); lf->extra = cloneString(bedRow[3]); lf->components = NULL; slAddHead(&list, lf); } /* Make sure this is sorted if in full mode. Sort by score when * coloring by score and in dense */ if (tg->visibility != tvDense) slSort(&list, linkedFeaturesCmpStart); else if ((tg->visibility == tvDense) && (chainCart->chainColor == chainColorScoreColors)) slSort(&list, chainCmpScore); else slReverse(&list); tg->items = list; bbiFileClose(&bbi); loadLinks(tg, winStart, winEnd, tvFull); maybeLoadSnake(tg); } void chainLoadItems(struct track *tg) /* Load up all of the chains from correct table into tg->items * item list. At this stage to conserve memory for other tracks * we don't load the links into the components list until draw time. */ { boolean doSnake = cartOrTdbBoolean(cart, tg->tdb, "doSnake" , FALSE); char *table = tg->table; struct chain chain; int rowOffset; char **row; struct sqlConnection *conn = hAllocConn(database); struct sqlResult *sr = NULL; struct linkedFeatures *list = NULL, *lf; int qs; char *optionChrStr; char extraWhere[128] ; struct cartOptions *chainCart; chainCart = (struct cartOptions *) tg->extraUiData; optionChrStr = skipLeadingSpaces(cartUsualStringClosestToHome(cart, tg->tdb, FALSE, "chromFilter", "All")); if (strlen(optionChrStr) > 0 && differentWord("All",optionChrStr)) { sqlSafef(extraWhere, sizeof(extraWhere), "qName = \"%s\" and score > %d",optionChrStr, chainCart->scoreFilter); sr = hRangeQuery(conn, table, chromName, winStart, winEnd, extraWhere, &rowOffset); } else { if (chainCart->scoreFilter > 0) { sqlSafef(extraWhere, sizeof(extraWhere), "score > \"%d\"",chainCart->scoreFilter); sr = hRangeQuery(conn, table, chromName, winStart, winEnd, extraWhere, &rowOffset); } else { sr = hRangeQuery(conn, table, chromName, winStart, winEnd, NULL, &rowOffset); } } while ((row = sqlNextRow(sr)) != NULL) { char buf[16]; chainHeadStaticLoad(row + rowOffset, &chain); AllocVar(lf); lf->start = lf->tallStart = chain.tStart; lf->end = lf->tallEnd = chain.tEnd; lf->qSize = chain.qSize; lf->grayIx = maxShade; if (chainCart->chainColor == chainColorScoreColors) { float normScore = sqlFloat((row+rowOffset)[11]); lf->grayIx = hGrayInRange(normScore, 0, 100, maxShade+1); lf->score = normScore; } else lf->score = chain.score; lf->filterColor = -1; if (chain.qStrand == '-') { lf->orientation = -1; qs = chain.qSize - chain.qEnd; } else { lf->orientation = 1; qs = chain.qStart; } int len = strlen(chain.qName) + 32; lf->name = needMem(len); if (!doSnake) safef(lf->name, len, "%s %c %dk", chain.qName, chain.qStrand, qs/1000); else safef(lf->name, len, "%s", chain.qName); safef(buf, sizeof(buf), "%d", chain.id); lf->extra = cloneString(buf); slAddHead(&list, lf); } /* Make sure this is sorted if in full mode. Sort by score when * coloring by score and in dense */ if (tg->visibility != tvDense) slSort(&list, linkedFeaturesCmpStart); else if ((tg->visibility == tvDense) && (chainCart->chainColor == chainColorScoreColors)) slSort(&list, chainCmpScore); else slReverse(&list); tg->items = list; loadLinks(tg, winStart, winEnd, tvFull); maybeLoadSnake(tg); /* Clean up. */ sqlFreeResult(&sr); hFreeConn(&conn); } /* chainLoadItems() */ static Color chainScoreColor(struct track *tg, void *item, struct hvGfx *hvg) { struct linkedFeatures *lf = (struct linkedFeatures *)item; return(tg->colorShades[lf->grayIx]); } static Color chainNoColor(struct track *tg, void *item, struct hvGfx *hvg) { return(tg->ixColor); } static void setNoColor(struct track *tg) { tg->itemColor = chainNoColor; tg->color.r = 0; tg->color.g = 0; tg->color.b = 0; +tg->color.a = 255; tg->altColor.r = 127; tg->altColor.g = 127; tg->altColor.b = 127; +tg->altColor.a = 255; tg->ixColor = MG_BLACK; tg->ixAltColor = MG_GRAY; } void chainMethods(struct track *tg, struct trackDb *tdb, int wordCount, char *words[]) /* Fill in custom parts of alignment chains. */ { struct cartOptions *chainCart; AllocVar(chainCart); boolean normScoreAvailable = chainDbNormScoreAvailable(tdb); /* what does the cart say about coloring option */ chainCart->chainColor = chainFetchColorOption(cart, tdb, FALSE); int scoreFilterDefault = atoi(trackDbSettingOrDefault(tdb, "scoreFilter", "0")); chainCart->scoreFilter = cartUsualIntClosestToHome(cart, tdb, FALSE, SCORE_FILTER, scoreFilterDefault); linkedFeaturesMethods(tg); tg->itemColor = lfChromColor; /* default coloring option */ tg->exonArrowsAlways = TRUE; /* if normScore column is available, then allow coloring */ if (normScoreAvailable) { switch (chainCart->chainColor) { case (chainColorScoreColors): tg->itemColor = chainScoreColor; tg->colorShades = shadesOfGray; break; case (chainColorNoColors): setNoColor(tg); break; default: case (chainColorChromColors): break; } } else { char *optionStr; /* this old option was broken before */ optionStr = cartUsualStringClosestToHome(cart, tdb, FALSE, "color", "on"); if (differentWord("on",optionStr)) { setNoColor(tg); chainCart->chainColor = chainColorNoColors; } else chainCart->chainColor = chainColorChromColors; } if (tg->isBigBed) tg->loadItems = bigChainLoadItems; else tg->loadItems = chainLoadItems; tg->drawItems = chainDraw; tg->mapItemName = lfMapNameFromExtra; tg->subType = lfSubChain; tg->extraUiData = (void *) chainCart; }