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/lib/trackHub.c src/hg/lib/trackHub.c
index ba379532c57..1949b8458dc 100644
--- src/hg/lib/trackHub.c
+++ src/hg/lib/trackHub.c
@@ -1048,30 +1048,31 @@
             // both places must be changed or common code created.
             if (!(startsWithWord("wig", type)||  startsWithWord("bedGraph", type)))
                 {
                 if (!(startsWithWord("bigWig", type) ||
                   startsWithWord("bigBed", type) ||
                   startsWithWord("pslSnake", type) ||
                   startsWithWord("halSnake", type) ||
                   startsWithWord("vcfTabix", type) ||
                   startsWithWord("vcfPhasedTrio", type) ||
                   startsWithWord("bigPsl", type) ||
                   startsWithWord("bigMaf", type) ||
                   startsWithWord("longTabix", type) ||
                   startsWithWord("bigGenePred", type) ||
                   startsWithWord("bigNarrowPeak", type) ||
                   startsWithWord("bigChain", type) ||
+                  startsWithWord("bigNet", type) ||
                   startsWithWord("bigLolly", type) ||
                   startsWithWord("bigBaseView", type) ||
                   startsWithWord("bigRmsk", type) ||
                   startsWithWord("bigBarChart", type) ||
                   startsWithWord("bigInteract", type) ||
                   startsWithWord("bigMethyl", type) ||
                   startsWithWord("hic", type) ||
                   startsWithWord("bigDbSnp", type) ||
                   startsWithWord("instaPort", type) ||
                   startsWithWord("bam", type)))
                     {
                     errAbort("Unsupported type '%s' in hub %s genome %s track %s", type,
                         hub->url, genome->name, tdb->track);
                     }
                 requiredSetting(hub, genome, tdb, "bigDataUrl");
@@ -1504,31 +1505,32 @@
 char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl");
 if (bigDataUrl != NULL)
     {
     char *type = trackDbRequiredSetting(tdb, "type");
     char *bigDataIndex = trackDbSetting(tdb, "bigDataIndex");
 
     verbose(2, "checking %s.%s type %s at %s\n", genome->name, tdb->track, type, bigDataUrl);
     if (startsWithWord("bigWig", type))
         {
         /* Just open and close to verify file exists and is correct type. */
         struct bbiFile *bbi = bigWigFileOpen(bigDataUrl);
         bbiFileClose(&bbi);
         }
     else if (startsWithWord("bigNarrowPeak", type) || startsWithWord("bigBed", type) ||
              startsWithWord("bigGenePred", type)  || startsWithWord("bigPsl", type)||
-             startsWithWord("bigChain", type)|| startsWithWord("bigMaf", type) ||
+             startsWithWord("bigChain", type)|| startsWithWord("bigNet", type) ||
+             startsWithWord("bigMaf", type) ||
              startsWithWord("bigBarChart", type) || startsWithWord("bigInteract", type) ||
              startsWithWord("bigLolly", type) || startsWithWord("bigRmsk",type) ||
              startsWithWord("bigMethyl", type))
         {
         /* Just open and close to verify file exists and is correct type. */
         struct bbiFile *bbi = bigBedFileOpen(bigDataUrl);
         char *typeString = cloneString(type);
         nextWord(&typeString);
         if (startsWithWord("bigBed", type) && (typeString != NULL))
             {
             unsigned numFields = sqlUnsigned(nextWord(&typeString));
             if (numFields > bbi->fieldCount)
                 errAbort("bigBed file '%s' has %d fields, but track \"%s\" declares 'type bigBed %d'. Either regenerate the bigBed with the correct number of fields, or change the type line to match the file's field count.", bigDataUrl, bbi->fieldCount, trackHubSkipHubName(tdb->track), numFields);
             }
         bbiFileClose(&bbi);