544f7054d812254a0883a412b98a08dcd78f1b84 braney Fri Sep 4 11:39:41 2026 -0700 hgTracks: draw quickLifted psl and bigPsl tracks pslTrack.c had no quickLift path at all. It now reads the alignments out of the assembly the track came from through quickLiftSql and maps them onto the reference. The chromFilter clause and the sort-and-filter tail moved into helpers so the two loaders cannot drift apart. bigBedTrack.c built the alignment out of the source interval but labelled it with the reference sequence name and never lifted it, so a quickLifted bigPsl track drew its items in the wrong place. It now takes the real source name out of the bigBed, lifts, counts a failure the way the bed path does, and rewrites the coordinate fields so a $chromStart in a mouseOver reports where the item is drawn. cds.c asked the assembly on screen for the CDS and for the sequence the alignment is to. Both belong to the assembly the alignment came from. This was already the wrong answer for a quickLifted track on any target, and on a hub-backed target it fails outright, because the name of such an assembly is not a database. The table-exists answer is now remembered per assembly rather than once for the process, since one page can hold both native and lifted alignment tracks. The mRNA filter reads the same source assembly, for the same reason: the accessions and the gbCdnaInfo ids are the source assembly's. refs #38249 diff --git src/hg/hgTracks/pslTrack.c src/hg/hgTracks/pslTrack.c index e8cad8bc691..d1b79ccc173 100644 --- src/hg/hgTracks/pslTrack.c +++ src/hg/hgTracks/pslTrack.c @@ -1,30 +1,32 @@ /* pslTrack - stuff to handle loading and display of * psl (blat format) based tracks. */ /* 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 "hCommon.h" #include "hash.h" #include "linefile.h" #include "jksql.h" #include "hdb.h" #include "hgTracks.h" #include "psl.h" #include "genbank.h" +#include "quickLift.h" +#include "trackHub.h" #ifndef GBROWSE #include "../gsid/gsidTable/gsidTable.h" #define SELECT_SUBJ "selectSubject" struct gsidSubj *gsidSelectedSubjList = NULL; struct gsidSeq *gsidSelectedSeqList = NULL; #endif /* GBROWSE */ int pslGrayIx(struct psl *psl, boolean isXeno, int maxShade) /* Figure out gray level for an RNA block. */ { double misFactor; double hitFactor; int res; @@ -89,31 +91,35 @@ if (*pLfList == NULL) return; type = cartUsualStringClosestToHome(cart, tg->tdb, FALSE, mud->filterTypeSuffix, "red"); if (sameString(type, "exclude")) isExclude = TRUE; else if (sameString(type, "include")) isExclude = FALSE; else colorIx = getFilterColor(type, MG_BLACK); type = cartUsualStringClosestToHome(cart, tg->tdb, FALSE, mud->logicTypeSuffix, "and"); andLogic = sameString(type, "and"); /* Make a pass though each filter, and start setting up search for * those that have some text. */ -conn = hAllocConn(database); +// For a quickLifted track the accessions, and the gbCdnaInfo ids the filter tables point +// at, belong to the assembly the alignments came from, not the one on screen. +char *liftDb = trackDbSetting(tg->tdb, "quickLiftDb"); +char *filterDb = (liftDb != NULL) ? liftDb : database; +conn = hAllocConn(filterDb); for (fil = mud->filterList; fil != NULL; fil = fil->next) { if (fil->pattern[0] != 0) // Filled above { fil->hash = newHash(10); if ((fil->mrnaTableIx = sqlFieldIndex(conn, gbCdnaInfoTable, skipDb(fil->table))) < 0) internalErr(); } } /* Scan tables id/name tables to build up hash of matching id's. */ for (fil = mud->filterList; fil != NULL; fil = fil->next) { struct hash *hash = fil->hash; int wordIx, wordCount; @@ -226,31 +232,38 @@ tg->limitedVisSet = FALSE; /* Need to recalculate this after filtering. */ /* Free up hashes, etc. */ for (fil = mud->filterList; fil != NULL; fil = fil->next) { hashFree(&fil->hash); } hFreeConn(&conn); } static boolean shouldFilterGenbankPatentSequences(struct track *tg) /* is this a genbank track with patent sequence filtering enabled */ { char name[256]; safef(name, sizeof(name), "%s.%s", tg->tdb->track, SHOW_PATENT_SEQUENCES_SUFFIX); -return (sameString(tg->tdb->track, "mrna")|| sameString(tg->tdb->track, "intronEst")) +// Strip the hub prefix on a quickLifted track so hub_NNN_mrna is recognized. The +// accession test itself needs no database, so it is right on a lifted track too. Leave +// other hubs alone: a hub track that merely happens to be named mrna was not being +// filtered before and should not start now. +char *bareTrack = tg->tdb->track; +if (trackDbSetting(tg->tdb, "quickLifted") != NULL) + bareTrack = trackHubSkipHubName(bareTrack); +return (sameString(bareTrack, "mrna")|| sameString(bareTrack, "intronEst")) && !cartUsualBoolean(cart, name, FALSE); } static void filterGenbankPatentSequences(struct track *tg, struct linkedFeatures **pLfList) /* remove genbank patent sequences */ { struct linkedFeatures *lf, *newLf = NULL; while ((lf = slPopHead(pLfList)) != NULL) { if (!isGenbankPatentAccession(lf->name)) slAddHead(&newLf, lf); } slReverse(&newLf); *pLfList = newLf; } @@ -320,84 +333,139 @@ lf->end = lf->tallEnd = psl->tEnd; /* Hang on to psl for use in drawing phase (this is why caller must not free psl!): */ lf->original = psl; return lf; } struct linkedFeatures *lfFromPsl(struct psl *psl, boolean isXeno) /* Create a linked feature item from psl. * Don't free psl afterwards! */ { return lfFromPslx(psl, 1, isXeno, FALSE, NULL); } +static char *pslChromFilterWhere(struct track *tg, char *extraWhere, int extraWhereSize) +/* Return the SQL clause for this track's chromFilter setting, written into extraWhere, + * or NULL when the setting asks for every chromosome. */ +{ +char optionChr[128]; /* Option - chromosome filter */ + +safef( optionChr, sizeof(optionChr), "%s.chromFilter", tg->track); +char *optionChrStr = cartUsualString(cart, optionChr, "All"); +if (!startsWith("chr",optionChrStr)) + return NULL; + +sqlSafef(extraWhere, extraWhereSize, "qName = \"%s\"",optionChrStr); +return extraWhere; +} + +static void finishPslLfList(struct track *tg, struct linkedFeatures *lfList) +/* Put the linked features built from an alignment table in order, apply the filters, and + * hang them on the track. */ +{ +slReverse(&lfList); +if (tg->visibility != tvDense) + slSort(&lfList, linkedFeaturesCmpStart); +if (tg->extraUiData) + filterMrna(tg, &lfList); +if (shouldFilterGenbankPatentSequences(tg)) + filterGenbankPatentSequences(tg, &lfList); +tg->items = lfList; +} + static void connectedLfFromPslsInRange(struct sqlConnection *conn, struct track *tg, int start, int end, char *chromName, boolean isXeno, boolean nameGetsPos, int sizeMul) /* Return linked features from range of table after have * already connected to database.. */ { struct sqlResult *sr = NULL; char **row; int rowOffset; -char *optionChrStr; struct linkedFeatures *lfList = NULL, *lf; -char optionChr[128]; /* Option - chromosome filter */ char extraWhere[128]; -safef( optionChr, sizeof(optionChr), "%s.chromFilter", tg->track); -optionChrStr = cartUsualString(cart, optionChr, "All"); -if (startsWith("chr",optionChrStr)) - { - sqlSafef(extraWhere, sizeof(extraWhere), "qName = \"%s\"",optionChrStr); - sr = hRangeQuery(conn, tg->table, chromName, start, end, extraWhere, &rowOffset); - } -else - { - safef(extraWhere, sizeof(extraWhere), " "); - sr = hRangeQuery(conn, tg->table, chromName, start, end, NULL, &rowOffset); - } +sr = hRangeQuery(conn, tg->table, chromName, start, end, + pslChromFilterWhere(tg, extraWhere, sizeof(extraWhere)), &rowOffset); if (sqlCountColumns(sr) < 21+rowOffset) errAbort("trackDb has incorrect table type for track \"%s\"", tg->track); while ((row = sqlNextRow(sr)) != NULL) { struct psl *psl = pslLoad(row+rowOffset); lf = lfFromPslx(psl, sizeMul, isXeno, nameGetsPos, tg); slAddHead(&lfList, lf); // Don't free psl - may be used by baseColor code (and freeing is slow) } -slReverse(&lfList); -if (tg->visibility != tvDense) - slSort(&lfList, linkedFeaturesCmpStart); -if (tg->extraUiData) - filterMrna(tg, &lfList); -if (shouldFilterGenbankPatentSequences(tg)) - filterGenbankPatentSequences(tg, &lfList); -tg->items = lfList; sqlFreeResult(&sr); +finishPslLfList(tg, lfList); +} + +static struct slList *pslRowLoader(char **row, int numFields) +/* Load one alignment out of a SQL row, in the shape quickLiftSql wants. */ +{ +return (struct slList *)pslLoad(row); +} + +static void quickLiftLfFromPsls(struct track *tg, char *chrom, int start, int end, + boolean isXeno, boolean nameGetsPos) +/* Read alignments out of the assembly the track was lifted from, map them onto the + * reference, and turn them into linked features. */ +{ +char *liftDb = trackDbSetting(tg->tdb, "quickLiftDb"); +char *table = NULL; +quickLiftResolveTable(tg->tdb, tg->table, &table, &liftDb); +char *quickLiftFile = trackDbSetting(tg->tdb, "quickLiftUrl"); +char extraWhere[128]; + +struct hash *chainHash = newHash(8); +struct sqlConnection *conn = hAllocConn(liftDb); +struct psl *pslList = (struct psl *)quickLiftSql(conn, quickLiftFile, table, + chrom, start, end, NULL, pslChromFilterWhere(tg, extraWhere, sizeof(extraWhere)), + pslRowLoader, 0, chainHash); +hFreeConn(&conn); + +struct linkedFeatures *lfList = NULL; +struct psl *psl, *nextPsl; +for(psl = quickLiftPsls(chainHash, pslList); psl != NULL; psl = nextPsl) + { + nextPsl = psl->next; + psl->next = NULL; // lfFromPslx hangs on to the psl, so don't leave it in a list + + // sizeMul is 1 whatever the caller passed: the lift returns an untranslated + // alignment, and it puts a protein alignment into nucleotide space on the way, so the + // block sizes are already in bases. + slAddHead(&lfList, lfFromPslx(psl, 1, isXeno, nameGetsPos, tg)); + } +finishPslLfList(tg, lfList); } static void lfFromPslsInRange(struct track *tg, int start, int end, - char *chromName, boolean isXeno, boolean nameGetsPos, int sizeMul) + char *chrom, boolean isXeno, boolean nameGetsPos, int sizeMul) /* Return linked features from range of table. */ { +if (trackDbSetting(tg->tdb, "quickLiftDb") != NULL) + { + quickLiftLfFromPsls(tg, chrom, start, end, isXeno, nameGetsPos); + return; + } + struct sqlConnection *conn = hAllocConn(database); -connectedLfFromPslsInRange(conn, tg, start, end, chromName, +connectedLfFromPslsInRange(conn, tg, start, end, chrom, isXeno, nameGetsPos, sizeMul); hFreeConn(&conn); } static void loadXenoPslWithPos(struct track *tg) /* load up all of the psls from correct table into tg->items item list*/ { lfFromPslsInRange(tg, winStart,winEnd, chromName, TRUE, TRUE, 1); } void pslChromMethods(struct track *tg, char *colorChromDefault) /* Fill in custom parts of xeno psl track */ { char option[128]; /* Option - rainbow chromosome color */ char *optionStr ;