0529f8de2a72011dfcea8f9a1221b6d227341743 braney Tue Mar 10 17:34:58 2026 -0700 hgc MAF click: use mafFrag to show only selected species with inserts removed Change the hgc MAF detail page to use hgMafFrag/hgBigMafFrag instead of displaying raw MAF blocks. This stitches alignments into a single continuous display in reference coordinates, filtering to only selected species and removing insertion columns where the reference has gaps. Key changes: - mafClick.c: rewrite mafOrAxtClick2 to build species orderList from trackDb settings (speciesOrder, speciesGroup, speciesUseFile) respecting speciesDefaultOff and cart on/off state via cartUsualBooleanClosestToHome, matching the hgTracks species selection logic in newSpeciesItems(). Add mafStripRefGaps() to remove insertion columns from mafFrag output. Show spaces instead of dots for matching bases in diff mode. - hgMaf.h/hgMaf.c: add hgMafFragFromMafList() public wrapper for pre-loaded mafLists (AXT/custom mafFile support). Change hgMafFragHelper to skip species not in orderList instead of errAbort. Track per-species source coordinates (src, start, end, srcSize, strand) in struct oneOrg so browser/DNA links work correctly in mafFrag output. Co-Authored-By: Claude Opus 4.6 diff --git src/hg/lib/hgMaf.c src/hg/lib/hgMaf.c index 1ac33026595..e69f969cc2c 100644 --- src/hg/lib/hgMaf.c +++ src/hg/lib/hgMaf.c @@ -140,30 +140,35 @@ for (maf = mafList; maf != NULL; maf = maf->next) { if (maf->components->strand != strand) errAbort("maf first component isn't %c", strand); } } struct oneOrg /* Info on one organism. */ { struct oneOrg *next; char *name; /* Name - allocated in hash */ int order; /* Help sort organisms. */ struct dyString *dy; /* Associated alignment for this organism. */ boolean hit; /* Flag to see if hit this time around. */ + char *src; /* Full src from first maf component (e.g. "mm10.chr5") */ + int start; /* Start in source from first aligned block. */ + int end; /* End in source (updated as we see more blocks). */ + int srcSize; /* Source sequence size. */ + char strand; /* Strand of alignment. */ }; static int oneOrgCmp(const void *va, const void *vb) /* Compare to sort based on order. */ { const struct oneOrg *a = *((struct oneOrg **)va); const struct oneOrg *b = *((struct oneOrg **)vb); return a->order - b->order; } static void fillInMissing(struct oneOrg *nativeOrg, struct oneOrg *orgList, struct dnaSeq *native, int seqStart, int curPos, int aliStart) /* Fill in alignment strings in orgList with native sequence * for first organism, and dots for rest. */ { @@ -380,47 +385,57 @@ { // if we found a dot, try the longer name len = e - mc->src; if (len >= sizeof(buf)) errAbort("organism/database name %s too long", mc->src); memcpy(buf, mc->src, len); buf[len] = 0; org = hashFindVal(orgHash, orgName); } } } else org = hashFindVal(orgHash, orgName); } - /* create a new org if necessary. */ + /* create a new org if necessary, or skip if not in orderList */ if (org == NULL) { if (orderList != NULL) - errAbort("%s is not in orderList", orgName); + continue; AllocVar(org); slAddHead(&orgList, org); hashAddSaveName(orgHash, orgName, org, &org->name); org->dy = dyStringNew(native->size*1.5); dyStringAppendMultiC(org->dy, '.', symCount); if (nativeOrg == NULL) nativeOrg = org; } if (orderList == NULL && order > org->order) org->order = order; org->hit = TRUE; + /* Track source coordinates for this organism */ + if (org->src == NULL) + { + org->src = cloneString(mc->src); + org->start = mc->start; + org->srcSize = mc->srcSize; + org->strand = mc->strand; + } + org->end = mc->start + mc->size; + /* Fill it up with alignment. */ dyStringAppendN(org->dy, mc->text, subMaf->textSize); } for (org = orgList; org != NULL; org = org->next) { if (!org->hit) dyStringAppendMultiC(org->dy, '.', subMaf->textSize); org->hit = FALSE; } symCount += subMaf->textSize; curPos = mcMaster->start + mcMaster->size; if (subMaf != maf) mafAliFree(&subMaf); } } @@ -454,39 +469,50 @@ mc->strand = '+'; mc->start = 0; mc->size = native->size; } else { mc->src = cloneString(masterSrc); mc->srcSize = chromSize; mc->strand = strand; if (strand == '-') reverseIntRange(&start, &end, chromSize); mc->start = start; mc->size = end-start; } } + else + { + if (org->src != NULL) + { + mc->src = cloneString(org->src); + mc->srcSize = org->srcSize; + mc->strand = org->strand; + mc->start = org->start; + mc->size = org->end - org->start; + } else { int size = countAlpha(org->dy->string); mc->src = cloneString(org->name); mc->srcSize = size; mc->strand = '+'; mc->start = 0; mc->size = size; } + } mc->text = cloneString(org->dy->string); dyStringFree(&org->dy); slAddHead(&maf->components, mc); } slReverse(&maf->components); slFreeList(&orgList); freeHash(&orgHash); return maf; } struct mafAli *hgBigMafFrag( char *database, /* Database, must already have hSetDb to this */ struct bbiFile *bbi, char *chrom, /* Chromosome (in database genome) */ @@ -509,30 +535,47 @@ char strand, /* Chromosome strand. */ char *outName, /* Optional name to use in first component */ struct slName *orderList /* Optional order of organisms. */ ) /* hgMafFrag- Extract maf sequences for a region from database and call hgMafFragHelper. */ { struct sqlConnection *conn = hAllocConn(database); struct mafAli *mafList = mafLoadInRegion(conn, track, chrom, start, end); struct mafAli *ret = hgMafFragHelper(database, chrom, start, end, strand, mafList, outName, orderList); hFreeConn(&conn); return ret; } +struct mafAli *hgMafFragFromMafList( + char *database, /* Database, must already have hSetDb to this */ + char *chrom, /* Chromosome (in database genome) */ + int start, int end, /* start/end in chromosome */ + char strand, /* Chromosome strand. */ + struct mafAli *mafList, /* Pre-loaded list of maf alignments */ + char *outName, /* Optional name to use in first component */ + struct slName *orderList /* Optional order of organisms. */ + ) +/* Extract maf sequences for a region from a pre-loaded mafList. + * Same behavior as hgMafFrag but takes mafList directly instead + * of loading from database. Caller should not free mafList + * afterwards (it is consumed). */ +{ +return hgMafFragHelper(database, chrom, start, end, strand, mafList, outName, orderList); +} + struct consWiggle *wigMafWiggles(char *db, struct trackDb *tdb) /* get conservation wiggle table names and labels from trackDb setting, ignoring those where table doesn't exist */ { char *fields[20]; int fieldCt; int i; char *wigTable, *wigLabel; struct consWiggle *wig, *wigList = NULL; char *setting = trackDbSetting(tdb, CONS_WIGGLE); if (!setting) return NULL; fieldCt = chopLine(cloneString(setting), fields); for (i = 0; i < fieldCt; i += 3) {