4d91bce1e029162895c6acde3100bd375ceee24e braney Tue Mar 10 18:14:44 2026 -0700 Gate new mafFrag MAF click display behind hg.conf mafClickMafFrag setting The new stitched mafFrag display (species filtering, insertion removal, space-for-match in diff mode) is now controlled by the hg.conf boolean mafClickMafFrag. When off (default), the original block-by-block display is used. Set mafClickMafFrag=on to enable the new behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> diff --git src/hg/hgc/mafClick.c src/hg/hgc/mafClick.c index 05679b5be12..950171b68d2 100644 --- src/hg/hgc/mafClick.c +++ src/hg/hgc/mafClick.c @@ -6,48 +6,54 @@ #include "common.h" #include "hash.h" #include "linefile.h" #include "hgc.h" #include "maf.h" #include "obscure.h" #include "cheapcgi.h" #include "genePred.h" #include "botDelay.h" #include "hgMaf.h" #include "hui.h" #include "hCommon.h" #include "hubConnect.h" #include "trackHub.h" #include "chromAlias.h" +#include "hgConfig.h" extern boolean issueBotWarning; #define ADDEXONCAPITAL /* Javascript to help make a selection from a drop-down * go back to the server. */ static char *autoSubmit = "document.gpForm.submit();"; static void blueCapWrite(FILE *f, char *s, int size, char *r) /* Write capital letters in blue. */ { boolean isBlue = FALSE; int i; for (i=0; i<size; ++i) { if (r!=NULL && s[i]==r[i]) + { + if (cfgOptionBooleanDefault("mafClickMafFrag", FALSE)) fprintf(f, " "); + else + fprintf(f, "."); + } else { char c = s[i]; if (isupper(c)) { if (!isBlue) { fprintf(f, "<span style='color:#0000FF;'>"); isBlue = TRUE; } } else if (islower(c)) { if (isBlue) { @@ -531,42 +537,45 @@ static void conservationStatsLink(struct trackDb *tdb, char *label, char *table) /* write link that to display statistics of phastCons table */ { char *chrom = cartCgiUsualString(cart, "c", "chr7"); printf("<A HREF=\"%s&g=%s&i=%s&c=%s&l=%d&r=%d&o=%d&db=%s" "&parentWigMaf=%s\" TARGET=\"_blank\">%s</A>", hgcPathAndSettings(), table, table, chrom, winStart, winEnd, winStart, database, tdb->track, label); } static void mafOrAxtClick2(struct sqlConnection *conn, struct sqlConnection *conn2, struct trackDb *tdb, char *axtOtherDb, char *fileName) /* Display details for MAF or AXT tracks. */ { +boolean useMafFrag = cfgOptionBooleanDefault("mafClickMafFrag", FALSE); if (issueBotWarning) { char *ip = getenv("REMOTE_ADDR"); botDelayMessage(ip, botDelayMillis); } if (winEnd - winStart > 30000) { printf("Zoom so that window is 30,000 bases or less to see alignments and conservation statistics\n"); } else { - struct mafAli *maf = NULL; + struct mafAli *mafList = NULL, *maf, *subList = NULL; + int aliIx = 0, realCount = 0; + char dbChrom[64]; char option[128]; char *capTrack; struct consWiggle *consWig, *consWiggles; struct hash *speciesOffHash = NULL; char *speciesOrder = NULL; char *speciesTarget = trackDbSetting(tdb, SPECIES_TARGET_VAR); char buffer[1024]; int useTarg = FALSE; int useIrowChains = FALSE; struct hash *labelHash = mafGetLabelHash(tdb); struct slName *orderList = NULL; safef(option, sizeof(option), "%s.%s", tdb->track, MAF_CHAIN_VAR); if (cartCgiUsualBoolean(cart, option, FALSE) && trackDbSetting(tdb, "irows") != NULL) @@ -574,31 +583,35 @@ safef(buffer, sizeof(buffer), "%s.vis",tdb->track); if (useIrowChains) { if (!cartVarExists(cart, buffer) && (speciesTarget != NULL)) useTarg = TRUE; else { char *val; val = cartUsualString(cart, buffer, "useCheck"); useTarg = sameString("useTarg",val); } } - /* Determine species order from trackDb settings, matching hgTracks logic */ + if (useMafFrag) + { + /* New mafFrag code path: determine species order from trackDb settings, + * matching hgTracks logic, then use hgMafFrag to stitch into a single + * continuous alignment in reference coordinates */ char *speciesGroup = trackDbSetting(tdb, SPECIES_GROUP_VAR); char *speciesUseFile = trackDbSetting(tdb, SPECIES_USE_FILE); speciesOrder = trackDbSetting(tdb, SPECIES_ORDER_VAR); /* Check cart override for speciesOrder */ safef(option, sizeof(option), "%s.speciesOrder", tdb->track); char *cartOrder = cartUsualString(cart, option, NULL); if (cartOrder != NULL) speciesOrder = cartOrder; if (speciesUseFile) speciesOrder = cartGetOrderFromFile(database, cart, speciesUseFile); /* Build hash of species that default to off */ char *speciesOff = trackDbSetting(tdb, SPECIES_DEFAULT_OFF_VAR); @@ -667,41 +680,160 @@ if (sameString(tdb->type, "bigMaf")) { char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl"); struct bbiFile *bbi = bigBedFileOpenAlias(bigDataUrl, chromAliasFindAliases); maf = hgBigMafFrag(database, bbi, seqName, winStart, winEnd, '+', NULL, orderList); bbiFileClose(&bbi); } else if (axtOtherDb == NULL && fileName == NULL) { /* Regular MAF from database */ maf = hgMafFrag(database, tdb->table, seqName, winStart, winEnd, '+', NULL, orderList); } else { /* AXT or MAF with external file - load blocks, then stitch */ - struct mafAli *mafList = mafOrAxtLoadInRegion2(conn, conn2, tdb, seqName, + mafList = mafOrAxtLoadInRegion2(conn, conn2, tdb, seqName, winStart, winEnd, axtOtherDb, fileName); maf = hgMafFragFromMafList(database, seqName, winStart, winEnd, '+', mafList, NULL, orderList); + mafList = NULL; /* consumed by hgMafFragFromMafList */ } /* Remove insertion columns (where reference has gaps) */ if (maf != NULL) mafStripRefGaps(maf); if (maf != NULL) + slAddHead(&subList, maf); + realCount = (subList != NULL) ? 1 : 0; + } + else + { + /* Original block-by-block code path */ + if (sameString(tdb->type, "bigMaf")) + { + char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl"); + struct bbiFile *bbi = bigBedFileOpenAlias(bigDataUrl, chromAliasFindAliases); + mafList = bigMafLoadInRegion(bbi, seqName, winStart, winEnd); + } + else + mafList = mafOrAxtLoadInRegion2(conn, conn2, tdb, seqName, winStart, winEnd, + axtOtherDb, fileName); + safef(dbChrom, sizeof(dbChrom), "%s.%s", hubConnectSkipHubPrefix(database), seqName); + + safef(option, sizeof(option), "%s.speciesOrder", tdb->track); + speciesOrder = cartUsualString(cart, option, NULL); + if (speciesOrder == NULL) + speciesOrder = trackDbSetting(tdb, "speciesOrder"); + + int speciesCt = 0; + char *species[2048]; + struct mafComp **newOrder; + if (speciesOrder) + { + speciesCt = chopLine(cloneString(speciesOrder), species); + newOrder = needMem((speciesCt + 1) * sizeof (struct mafComp *)); + + int ii; + struct hash *nameHash = newHash(5); + for(ii=0; ii < speciesCt; ii++) + { + if (hashLookup(nameHash, species[ii])) + errAbort("speciesOrder contains %s more than once.", species[ii]); + hashStore(nameHash, species[ii]); + } + } + + for (maf = mafList; maf != NULL; maf = maf->next) + { + int mcCount = 0; + struct mafComp *mc; + struct mafAli *subset; + struct mafComp *nextMc; + + if (!useTarg) + { + for (mc = maf->components->next; mc != NULL; mc = nextMc) + { + char buf[64]; + char *organism; + mafSrcDb(mc->src, buf, sizeof buf); + organism = hOrganism(buf); + if (!organism) + organism = buf; + nextMc = mc->next; + safef(option, sizeof(option), "%s.%s", tdb->track, buf); + if (!cartUsualBoolean(cart, option, TRUE)) + { + if (speciesOffHash == NULL) + speciesOffHash = newHash(4); + hashStoreName(speciesOffHash, organism); + } + if (!cartUsualBoolean(cart, option, TRUE)) + slRemoveEl(&maf->components, mc); + else + mcCount++; + } + } + if (mcCount == 0) + continue; + + if (speciesCt) + { + struct mafComp *mcThis; + int i; + + mcCount = 0; + speciesCt = chopLine(cloneString(speciesOrder), species); + newOrder = needMem((speciesCt + 1) * sizeof (struct mafComp *)); + newOrder[mcCount++] = maf->components; + + for (i = 0; i < speciesCt; i++) + { + if ((mcThis = mafMayFindCompSpecies(maf, species[i], '.')) == NULL) + continue; + if (mcThis == maf->components) + errAbort("Reference species (%s) shouldn't be in speciesOrder in trackDb", species[i]); + newOrder[mcCount++] = mcThis; + } + + maf->components = NULL; + for (i = 0; i < mcCount; i++) + { + newOrder[i]->next = 0; + slAddHead(&maf->components, newOrder[i]); + } + + slReverse(&maf->components); + } + subset = mafSubsetE(maf, dbChrom, winStart, winEnd, TRUE); + if (subset != NULL) + { + mafMoveComponentToTop(subset, dbChrom); + if (subset->components->strand == '-') + mafFlipStrand(subset); + subset->score = mafScoreMultiz(subset); + slAddHead(&subList, subset); + ++realCount; + } + } + slReverse(&subList); + mafAliFreeList(&mafList); + } + + if (subList != NULL) { char *showVarName = "hgc.showMultiBase"; char *showVarVal = cartUsualString(cart, showVarName, "all"); boolean onlyDiff = sameWord(showVarVal, "diff"); #ifdef ADDEXONCAPITAL char *codeVarName = "hgc.multiCapCoding"; char *codeVarVal = cartUsualString(cart, codeVarName, "coding"); boolean onlyCds = sameWord(codeVarVal, "coding"); #endif /* add links for conservation score statistics */ consWiggles = wigMafWiggles(database, tdb); int wigCount = slCount(consWiggles); if (wigCount == 1) { conservationStatsLink(tdb, "Conservation score statistics", consWiggles->table); @@ -803,41 +935,49 @@ puts("for aligned species, click on 'D' to get DNA for aligned species.<BR>"); printf("<TT><PRE>"); /* notify if species removed from alignment */ if (speciesOffHash) { char *species; struct hashCookie hc = hashFirst(speciesOffHash); puts("<B>Components not displayed:</B> "); while ((species = hashNextName(&hc)) != NULL) printf("%s ", species); puts("<BR>"); } + for (maf = subList; maf != NULL; maf = maf->next) + { mafLowerCase(maf); #ifdef ADDEXONCAPITAL if (capTrack != NULL) capMafOnTrack(maf, capTrack, onlyCds); #endif + if (useMafFrag) printf("<B>Alignment %d - %d, %d bps </B>\n", maf->components->start + 1, maf->components->start + maf->components->size, maf->components->size); - mafPrettyOut(stdout, maf, 70, onlyDiff, 1, labelHash); - mafAliFree(&maf); + else + printf("<B>Alignment block %d of %d in window, %d - %d, %d bps </B>\n", + ++aliIx,realCount,maf->components->start + 1, + maf->components->start + maf->components->size, maf->components->size); + mafPrettyOut(stdout, maf, 70, onlyDiff, aliIx, labelHash); + } + mafAliFreeList(&subList); } else { printf("No multiple alignment in browser window"); } printf("</PRE></TT>"); slNameFreeList(&orderList); } } static void mafOrAxtClick(struct sqlConnection *conn, struct trackDb *tdb, char *axtOtherDb) { struct sqlConnection *conn2 = NULL; if (!(isHubTrack(tdb->track) || trackHubDatabase(database))) conn2 = hAllocConn(database);