353a34ac7e7638457db3f55e57452069113d860b braney Sun Sep 6 13:34:28 2026 -0700 Add a bigNet track type, a net of alignments in a bigBed, refs #20824 Track hubs have had no way to show a real net. The usual stand-in is a net rendered as a maf, which loses the level structure that makes a net useful for establishing orthologous sequence. bigNet holds the netAlign columns in a bigBed, so a hub can carry the net itself. The format is bed6+20: the target in chrom/chromStart/chromEnd, the query sequence in name, the query strand in strand, then level and the rest of the netAlign fields. The trackDb line is type bigNet <targetDb> <chainTrack> mirroring type netAlign. chainTrack is the plain trackDb name of the bigChain track in the same hub; hgc adds the hub prefix itself. chainNetLoadRangeHub() builds a chainNet from a bigBed range query and hands it to the same helpToNet() the SQL path uses, so the nesting is rebuilt the same way. netDraw picks its loader off tg->isBigBed and the drawing code below that is untouched. genericNetClick does the same for the details page and follows the named chain track for the alignment. Also bounds the level walk in helpToNet() by help->maxDepth. It could read one past the end of the levels array. netToBigNet converts a net file to bedToBigBed input. It writes the tab line itself rather than calling bigNetTabOut, because autoSql prints a double with %g and that drops digits off a chain score. diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c index fab157dbb55..efca28e8780 100644 --- src/hg/hgc/hgc.c +++ src/hg/hgc/hgc.c @@ -135,30 +135,31 @@ #include "sample.h" #include "axt.h" #include "axtInfo.h" #include "jaxQTL.h" #include "jaxQTL3.h" #include "wgRna.h" #include "ncRna.h" #include "gbProtAnn.h" #include "hgSeq.h" #include "chain.h" #include "chainDb.h" #include "chainNetDbLoad.h" #include "chainToPsl.h" #include "chainToAxt.h" #include "netAlign.h" +#include "bigNet.h" #include "stsMapRat.h" #include "stsInfoRat.h" #include "stsMapMouseNew.h" #include "stsInfoMouseNew.h" #include "vegaInfo.h" #include "vegaInfoZfish.h" #include "ensInfo.h" #include "scoredRef.h" #include "blastTab.h" #include "hdb.h" #include "hgc.h" #include "genbank.h" #include "pseudoGeneLink.h" #include "axtLib.h" #include "ensFace.h" @@ -4215,64 +4216,145 @@ } void printLabeledPercent(char *org, char *label, long p, long q) /* Print label: in bold, then p, and then 100 * p/q */ { char *space = " "; if (org == NULL) org = space = ""; printf("<B>%s%s%s:</B> ", org, space, label); printLongWithCommas(stdout, p); if (q != 0) printf(" (%3.1f%%)", 100.0 * p / q); printf("<BR>\n"); } +static struct netAlign *bigNetLoadOne(struct trackDb *tdb, char *chrom, int start, + unsigned level) +/* Load the record from a bigNet file at the given level that covers start. + * Returns NULL if there isn't one. */ +{ +char *fileName = hReplaceGbdb(trackDbSetting(tdb, "bigDataUrl")); +if (fileName == NULL) + errAbort("No bigDataUrl in track %s", tdb->track); +struct lm *lm = lmInit(0); +struct bbiFile *bbi = bigBedFileOpenAlias(fileName, chromAliasFindAliases); +struct bigBedInterval *bb, *bbList = bigBedIntervalQuery(bbi, chrom, start, start+1, 0, lm); +char *bedRow[BIGNET_NUM_COLS]; +char startBuf[16], endBuf[16]; +struct netAlign *na = NULL; + +for (bb = bbList; bb != NULL; bb = bb->next) + { + struct bigNet bn; + int fieldCount = bigBedIntervalToRow(bb, chrom, startBuf, endBuf, bedRow, ArraySize(bedRow)); + if (fieldCount != BIGNET_NUM_COLS) + errAbort("%s has %d fields, bigNet needs %d", fileName, fieldCount, BIGNET_NUM_COLS); + bigNetStaticLoad(bedRow, &bn); + if (bn.level != level) + continue; + AllocVar(na); + na->level = bn.level; + na->tName = cloneString(bn.chrom); + na->tStart = bn.chromStart; + na->tEnd = bn.chromEnd; + safecpy(na->strand, sizeof na->strand, bn.strand); + na->qName = cloneString(bn.name); + na->qStart = bn.qStart; + na->qEnd = bn.qEnd; + na->chainId = bn.chainId; + na->ali = bn.ali; + na->score = bn.chainScore; + na->qOver = bn.qOver; + na->qFar = bn.qFar; + na->qDup = bn.qDup; + na->type = cloneString(bn.type); + na->tN = bn.tN; + na->qN = bn.qN; + na->tR = bn.tR; + na->qR = bn.qR; + na->tNewR = bn.tNewR; + na->qNewR = bn.qNewR; + na->tOldR = bn.tOldR; + na->qOldR = bn.qOldR; + na->tTrf = bn.tTrf; + na->qTrf = bn.qTrf; + break; + } +bbiFileClose(&bbi); +lmCleanup(&lm); +return na; +} + +static char *netChainTrackName(struct trackDb *tdb, char *chainTrack) +/* The type line of a net track names its chain track without any hub prefix. + * If the net track came from a hub, so did the chain track it names. */ +{ +if (!isHubTrack(tdb->track)) + return chainTrack; +char buf[256]; +safef(buf, sizeof buf, "hub_%d_%s", hubIdFromTrackName(tdb->track), chainTrack); +return cloneString(buf); +} + void genericNetClick(struct sqlConnection *conn, struct trackDb *tdb, char *item, int start, char *otherDb, char *chainTrack) /* Generic click handler for net tracks. */ { char table[HDB_MAX_TABLE_STRING]; boolean hasBin; char query[256]; struct sqlResult *sr; char **row; struct netAlign *net; char *org = hOrganism(database); char *otherOrg = hOrganism(otherDb); char *otherOrgBrowser = otherOrg; int tSize, qSize; int netWinSize; struct chain *chain; +boolean isBig = startsWith("big", tdb->type); + +if (isBig) + chainTrack = netChainTrackName(tdb, chainTrack); if (otherOrg == NULL) { /* use first word in short track label */ otherOrg = firstWordInLine(cloneString(tdb->shortLabel)); } +if (isBig) + { + net = bigNetLoadOne(tdb, seqName, start, sqlUnsigned(item)); + if (net == NULL) + errAbort("Couldn't find %s:%d at level %s in %s", seqName, start, item, tdb->track); + } +else + { if (!hFindSplitTable(database, seqName, tdb->table, table, sizeof table, &hasBin)) errAbort("genericNetClick track %s not found", tdb->table); sqlSafef(query, sizeof(query), "select * from %s where tName = '%s' and tStart <= %d and tEnd > %d " "and level = %s", table, seqName, start, start, item); sr = sqlGetResult(conn, query); if ((row = sqlNextRow(sr)) == NULL) errAbort("Couldn't find %s:%d in %s", seqName, start, table); net = netAlignLoad(row+hasBin); sqlFreeResult(&sr); + } tSize = net->tEnd - net->tStart; qSize = net->qEnd - net->qStart; if (net->chainId != 0) { netWinSize = min(winEnd-winStart, net->tEnd - net->tStart); printf("<BR>\n"); /* Show alignment if the database exists and */ /* if there is a chromInfo table for that database and the sequence */ /* file exists. This means that alignments can be shown on the archive */ /* server (or in other cases) if there is a database with a chromInfo */ /* table, the sequences are available and there is an entry added to */ /* dbDb for the otherDb. */ if (chromSeqFileExists(otherDb, net->qName)) { @@ -4285,30 +4367,37 @@ char id[20]; snprintf(id, sizeof(id), "%d", net->chainId); hgcAnchorWindow("htcChainAli", id, ns, ne, chainTrack, seqName); printf("View alignment details of parts of net within browser window</A>.<BR>\n"); } else { printf("Odd, net not in window<BR>\n"); } } else { printf("To see alignment details zoom so that the browser window covers 1,000,000 bases or less.<BR>\n"); } } + if (isBig) + { + char idBuf[32]; + safef(idBuf, sizeof idBuf, "%u", net->chainId); + chain = chainLoadItemInRange(getTdbForTrackName(chainTrack), idBuf); + } + else chain = chainDbLoad(conn, database, chainTrack, seqName, net->chainId); if (chain != NULL) { /* print link to browser for otherDb only if otherDb is active */ if (hDbIsActive(otherDb)) chainToOtherBrowser(chain, otherDb, otherOrgBrowser, NULL); chainFree(&chain); } htmlHorizontalLine(); } printf("<B>Type:</B> %s<BR>\n", net->type); printf("<B>Level:</B> %d<BR>\n", (net->level+1)/2); printf("<B>%s position:</B> %s:%d-%d<BR>\n", org, net->tName, net->tStart+1, net->tEnd); printf("<B>%s position:</B> %s:%d-%d<BR>\n", @@ -5092,30 +5181,31 @@ } else if (!trackHubDatabase(database)) conn = hAllocConnTrack(database, tdb); if (itemForUrl == NULL) itemForUrl = item; dupe = cloneString(tdb->type); wordCount = chopLine(dupe, words); headerItem = cloneString(item); type = words[0]; /* Suppress printing item name in page header, as it is not informative for these types of * tracks... */ if (container == NULL && wordCount > 0) { if (sameString(type, "maf") || sameString(type, "wigMaf") || sameString(type, "bigMaf") || sameString(type, "netAlign") + || sameString(type, "bigNet") || sameString(type, "bigQuickLiftChain") || sameString(type, "encodePeak")) headerItem = NULL; else if (( sameString(type, "narrowPeak") || sameString(type, "broadPeak") || sameString(type, "gappedPeak") ) && headerItem && sameString(headerItem, ".") ) headerItem = NULL; } /* Print header. */ genericHeader(tdb, headerItem); if (differentString(type, "bigInteract") && differentString(type, "interact")) { @@ -5178,34 +5268,34 @@ pepTable = words[1]; if ((wordCount > 2) && !sameString(words[2], ".")) mrnaTable = words[2]; genericGenePredClick(conn, tdb, item, start, pepTable, mrnaTable); } else if ( sameString(type, "bigPsl")) { genericBigPslClick(conn, tdb, item, start, end); } else if (sameString(type, "psl")) { char *subType = "."; if (wordCount > 1) subType = words[1]; genericPslClick(conn, tdb, item, start, subType); } - else if (sameString(type, "netAlign")) + else if (sameString(type, "netAlign") || sameString(type, "bigNet")) { if (wordCount < 3) - errAbort("Missing field in netAlign track type field"); + errAbort("Missing field in %s track type field", type); genericNetClick(conn, tdb, item, start, words[1], words[2]); } else if (sameString(type, "bigQuickLiftChain")) { quickLiftChainClick(conn, tdb, item, start, words[1]); } else if (sameString(type, "chain") || sameString(type, "bigChain") ) { if (wordCount < 2) errAbort("Missing field in chain track type field"); genericChainClick(conn, tdb, item, start, words[1]); } else if (sameString(type, "maf")) { genericMafClick(conn, tdb, item, start);