a1cfd4eb6e2f1e0eb0e8756fd02ebcb602c77687
kent
  Tue Jun 28 13:08:30 2022 -0700
Making base-by-base alignment display work for bigChains and bigPsls.

diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c
index 8efffb4..0c22794 100644
--- src/hg/hgc/hgc.c
+++ src/hg/hgc/hgc.c
@@ -257,30 +257,31 @@
 #include "bPlusTree.h"
 #include "customFactory.h"
 #include "iupac.h"
 #include "clinvarSubLolly.h"
 #include "jsHelper.h"
 #include "errCatch.h"
 #include "htslib/bgzf.h"
 #include "htslib/kstring.h"
 #include "pipeline.h"
 #include "genark.h"
 #include "chromAlias.h"
 
 static char *rootDir = "hgcData";
 
 #define LINESIZE 70  /* size of lines in comp seq feature */
+#define MAX_DISPLAY_QUERY_SEQ_SIZE 5000000  // Big enough for HLA alts
 
 struct cart *cart;	/* User's settings. */
 char *seqName;		/* Name of sequence we're working on. */
 int winStart, winEnd;   /* Bounds of sequence. */
 char *database;		/* Name of mySQL database. */
 char *organism;		/* Colloquial name of organism. */
 char *genome;		/* common name, e.g. Mouse, Human */
 char *scientificName;	/* Scientific name of organism. */
 
 /* for earlyBotCheck() function at the beginning of main() */
 #define delayFraction   0.5    /* standard penalty is 1.0 for most CGIs */
                                 /* this one is 0.5 */
 boolean issueBotWarning = FALSE;
 
 struct hash *trackHash;	/* A hash of all tracks - trackDb valued */
@@ -3224,39 +3225,71 @@
 }
 
 void pslDumpHtml(struct psl *pslList)
 /* print out psl header and data */
 {
 struct psl* psl;
 printf("<PRE><TT>\n");
 printf("#match\tmisMatches\trepMatches\tnCount\tqNumInsert\tqBaseInsert\ttNumInsert\tBaseInsert\tstrand\tqName\tqSize\tqStart\tqEnd\ttName\ttSize\ttStart\ttEnd\tblockCount\tblockSizes\tqStarts\ttStarts\n");
 for (psl = pslList; psl != NULL; psl = psl->next)
     {
     pslTabOut(psl, stdout);
     }
 printf("</TT></PRE>\n");
 }
 
+struct twoBitFile *getTwoBitFileFromUrlInSettings(struct trackDb *tdb, char *settingName)
+/* Assume settingName is a URL to a two bit file and try and return it */
+{
+struct twoBitFile *tbf = NULL;
+char *url = trackDbSetting(tdb, settingName);
+if (url != NULL)
+   tbf = twoBitOpen(url);
+return tbf;
+}
+
+struct twoBitFile *getOtherTwoBitUrl(struct trackDb *tdb)
+/* Return open two bit file if otherTwoBitUrl setting is present */
+{
+return getTwoBitFileFromUrlInSettings(tdb, "otherTwoBitUrl");
+}
+
+struct psl *getPslAndSeq(struct trackDb *tdb, char *chromName, struct bigBedInterval *bb, 
+    unsigned seqTypeField, DNA **retSeq, char **retCdsStr)
+/* Read in psl and query sequence out of a bbiInverval on a give chromosome */
+{
+struct psl *psl= pslFromBigPsl(chromName, bb, seqTypeField, retSeq, retCdsStr);
+DNA *dna = *retSeq;
+if (dna == NULL)
+    {
+    struct twoBitFile *otherTbf = getOtherTwoBitUrl(tdb);
+    struct dnaSeq *seq = twoBitReadSeqFrag(otherTbf, psl->qName, 0, 0);
+    *retSeq = dna = seq->dna;
+    }
+return psl;
+}
+
 void genericBigPslClick(struct sqlConnection *conn, struct trackDb *tdb,
                      char *item, int start, int end)
 /* Handle click in big psl track. */
 {
 struct psl* pslList = NULL;
 char *fileName = bbiNameFromSettingOrTable(tdb, conn, tdb->table);
 struct bbiFile *bbi =  bigBedFileOpenAlias(fileName, chromAliasFindAliases);
 struct lm *lm = lmInit(0);
 int ivStart = start, ivEnd = end;
+
 if (start == end)
     {  
     // item is an insertion; expand the search range from 0 bases to 2 so we catch it:
     ivStart = max(0, start-1);
     ivEnd++;
     }  
 
 boolean showEvery = sameString(item, "PrintAllSequences");
 boolean showAll = trackDbSettingOn(tdb, "showAll");
 unsigned seqTypeField =  bbExtraFieldIndex(bbi, "seqType");
 struct bigBedInterval *bb, *bbList = NULL;
 
 // If showAll is on, show all alignments with this qName, not just the
 // selected one.
 if (showEvery)
@@ -3313,57 +3346,60 @@
 char startBuf[16], endBuf[16];
 
 int lastChromId = -1;
 char chromName[bbi->chromBpt->keySize+1];
 
 boolean firstTime = TRUE;
 for (bb = bbList; bb != NULL; bb = bb->next)
     {
     bbiCachedChromLookup(bbi, bb->chromId, lastChromId, chromName, sizeof(chromName));
 
     lastChromId=bb->chromId;
     bigBedIntervalToRow(bb, chromName, startBuf, endBuf, bedRow, 4);
     if (showEvery || sameString(bedRow[3], item))
 	{
         char *cdsStr, *seq;
-        struct psl *psl= pslFromBigPsl(chromName, bb, seqTypeField, &seq, &cdsStr);
+        struct psl *psl= getPslAndSeq(tdb, chromName, bb, seqTypeField, &seq, &cdsStr);
         slAddHead(&pslList, psl);
 
         // we're assuming that if there are multiple psl's with the same id that
         // they are the same query sequence so we only put out one set of sequences
         if (differentString(item, "PrintAllSequences") && firstTime && !isEmpty(seq))    // if there is a query sequence
             {
             firstTime = FALSE;
             printf("<H3>Links to sequence:</H3>\n");
             printf("<UL>\n");
 
             if (!isEmpty(cdsStr))  // if we have CDS 
                 {
                 puts("<LI>\n");
                 hgcAnchorSomewhere("htcTranslatedBigPsl", item, "translate", seqName);
                 printf("Translated Amino Acid Sequence</A> from Query Sequence\n");
                 puts("</LI>\n");
                 }
 
+	    if (psl->qSize <= MAX_DISPLAY_QUERY_SEQ_SIZE)
+		{
 		puts("<LI>\n");
 		hgcAnchorSomewhere("htcBigPslSequence", item, tdb->track, seqName);
 		printf("Query Sequence</A> \n");
 		puts("</LI>\n");
 		printf("</UL>\n");
 		}
             }
 	}
+    }
 
 char *sort = cartUsualString(cart, "sort", pslSortList[0]);
 pslSortListByVar(&pslList, sort);
 
 if (showEvery)
     printf("<H3>Genomic Alignments</H3>");
 else
     printf("<H3>%s/Genomic Alignments</H3>", item);
 if (showEvery || pslIsProtein(pslList))
     printAlignmentsSimple(pslList, start, "htcBigPslAli", tdb->table, item);
 else
     printAlignmentsExtra(pslList, start, "htcBigPslAli", "htcBigPslAliInWindow",
         tdb->table, item);
 pslFreeList(&pslList);
 printItemDetailsHtml(tdb, item);
@@ -3488,30 +3524,49 @@
     {
     htmlHorizontalLine();
 
     // Add pennantIcon
     printPennantIconNote(tdb);
 
     // Wrap description html in div with limited width, so when the page is very wide
     // due to long details, the user doesn't have to scroll right to read the description.
     puts("<div class='readableWidth'>");
     puts(html);
     puts("</div>");
     }
 hPrintf("<BR>\n");
 }
 
+struct chain *chainLoadItemInRange(struct trackDb *tdb, char *item)
+/* Load up parts of chain that intersect seqName:winStart-winEnd */
+{
+struct chain *chain = NULL;
+int id = sqlUnsigned(item);
+if (startsWith("big", tdb->type))
+    {
+    char *fileName = trackDbSetting(tdb, "bigDataUrl");
+    char *linkFileName = trackDbSetting(tdb, "linkDataUrl");
+    chain = chainLoadIdRangeHub(database, fileName, linkFileName, seqName, winStart, winEnd, id);
+    }
+else
+    {
+    chain = chainLoadIdRange(database, tdb->table, seqName, winStart, winEnd, id);
+    }
+return chain;
+}
+
+
 void qChainRangePlusStrand(struct chain *chain, int *retQs, int *retQe)
 /* Return range of bases covered by chain on q side on the plus
  * strand. */
 {
 if (chain == NULL)
     errAbort("Can't find range in null query chain.");
 if (chain->qStrand == '-')
     {
     *retQs = chain->qSize - chain->qEnd+1;
     *retQe = chain->qSize - chain->qStart;
     }
 else
     {
     *retQs = chain->qStart+1;
     *retQe = chain->qEnd;
@@ -3585,79 +3640,88 @@
 struct chain *subChain = NULL, *toFree = NULL;
 int qs,qe;
 chainSubsetOnT(chain, winStart, winEnd, &subChain, &toFree);
 if (subChain != NULL && otherOrg != NULL)
     {
     qChainRangePlusStrand(subChain, &qs, &qe);
     if (hubUrl)
         linkToOtherBrowserHub(otherDb, subChain->qName, qs-1, qe, hubUrl);
     else
         linkToOtherBrowser(otherDb, subChain->qName, qs-1, qe);
     printf("Open %s browser</A> at position corresponding to the part of chain that is in this window.<BR>\n", trackHubSkipHubName(otherOrg));
     }
 chainFree(&toFree);
 }
 
+struct dnaSeq *otherChromSeq(struct twoBitFile *otherTbf, char *otherDb, 
+    char *otherChrom, int otherStart, int otherEnd)
+/* This fetches sequence from tdb->otherTwoBitUrl if available,  if not
+ * tries to find it via otherDb */
+{
+if (otherTbf != NULL)
+    {
+    return twoBitReadSeqFrag(otherTbf, otherChrom, otherStart, otherEnd);
+    }
+else
+    {
+    return hChromSeq(otherDb, otherChrom, otherStart, otherEnd);
+    }
+}
+
 void genericChainClick(struct sqlConnection *conn, struct trackDb *tdb,
                        char *item, int start, char *otherDb)
 /* Handle click in chain track, at least the basics. */
 {
+struct twoBitFile *otherTbf = getOtherTwoBitUrl(tdb);
 char *thisOrg = hOrganism(database);
 char *otherOrg = NULL;
 struct chain *chain = NULL, *subChain = NULL, *toFree = NULL;
 int chainWinSize;
 double subSetScore = 0.0;
 int qs, qe;
 boolean nullSubset = FALSE;
 boolean otherIsActive = FALSE;
 char *hubUrl = NULL;   // if otherDb is a genark browser
 
 if (hDbIsActive(otherDb))
     otherIsActive = TRUE;
 else 
     hubUrl = genarkUrl(otherDb); // may be NULL
 
 if (! sameWord(otherDb, "seq"))
     {
     otherOrg = hOrganism(otherDb);
     }
 if (otherOrg == NULL)
     {
     /* use first word of chain label (count on org name as first word) */
     otherOrg = firstWordInLine(cloneString(tdb->shortLabel));
     }
 
+chain = chainLoadItemInRange(tdb, item);
 if (startsWith("big", tdb->type))
     {
-    char *fileName = bbiNameFromSettingOrTable(tdb, conn, tdb->table);
-    char *linkFileName = trackDbSetting(tdb, "linkDataUrl");
-    chain = chainLoadIdRangeHub(database, fileName, linkFileName, seqName, winStart, winEnd, atoi(item));
-
     if (!otherIsActive) // if this isn't a native database, check to see if it's a hub
         {
         struct trackHubGenome *genome = trackHubGetGenomeUndecorated(otherDb);
         if (genome)
             {
             otherIsActive = TRUE;
             otherDb = genome->name;
             }
         }
     }
-else
-    {
-    chain = chainLoadIdRange(database, tdb->table, seqName, winStart, winEnd, atoi(item));
-    }
 
 chainSubsetOnT(chain, winStart, winEnd, &subChain, &toFree);
 
 if (subChain == NULL)
     nullSubset = TRUE;
 else if (otherIsActive && subChain != chain)
     {
     char *linearGap = trackDbSettingOrDefault(tdb, "chainLinearGap", "loose");
     struct gapCalc *gapCalc = gapCalcFromFile(linearGap);
     struct axtScoreScheme *scoreScheme = axtScoreSchemeDefault();
     int qStart = subChain->qStart;
     int qEnd   = subChain->qEnd  ;
     struct dnaSeq *tSeq = hDnaFromSeq(database, subChain->tName, subChain->tStart, subChain->tEnd, dnaLower);
     struct dnaSeq *qSeq = NULL;
     char *matrix = trackDbSetting(tdb, "matrix");
@@ -3670,31 +3734,31 @@
             scoreScheme = axtScoreSchemeFromBlastzMatrix(words[1], 400, 30);
             }
         else
             {
             if (size != 2)
                 errAbort("error parsing matrix entry in trackDb, expecting 2 word got %d ",
                         size);
             else
                 errAbort("error parsing matrix entry in trackDb, size 16 matrix, got %d ",
                         atoi(words[0]));
             }
         }
 
     if (subChain->qStrand == '-')
         reverseIntRange(&qStart, &qEnd, subChain->qSize);
-    qSeq = hChromSeq(otherDb, subChain->qName, qStart, qEnd);
+    qSeq = otherChromSeq(otherTbf, otherDb, subChain->qName, qStart, qEnd);
     if (subChain->qStrand == '-')
         reverseComplement(qSeq->dna, qSeq->size);
     subChain->score = chainCalcScoreSubChain(subChain, scoreScheme, gapCalc,
         qSeq, tSeq);
     subSetScore = subChain->score;
     }
 chainFree(&toFree);
 
 printf("<B>%s position:</B> <A HREF=\"%s?%s&db=%s&position=%s:%d-%d\">%s:%d-%d</A>"
        "  size: %d <BR>\n",
        trackHubSkipHubName(thisOrg), hgTracksName(), cartSidUrlString(cart), database,
        chain->tName, chain->tStart+1, chain->tEnd, chain->tName, chain->tStart+1, chain->tEnd,
        chain->tEnd-chain->tStart);
 printf("<B>Strand:</B> %c<BR>\n", chain->qStrand);
 qChainRangePlusStrand(chain, &qs, &qe);
@@ -3751,31 +3815,33 @@
         }
     sqlFreeResult(&sr);
     printf("<BR>\n");
     }
 
 printf("<BR>Fields above refer to entire chain or gap, not just the part inside the window.<BR>\n");
 printf("<BR>\n");
 
 chainWinSize = min(winEnd-winStart, chain->tEnd - chain->tStart);
 /* 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 (!startsWith("big", tdb->type) && sqlDatabaseExists(otherDb) && chromSeqFileExists(otherDb, chain->qName))
+if (otherTbf != NULL || 
+    (!startsWith("big", tdb->type) && sqlDatabaseExists(otherDb) 
+     && chromSeqFileExists(otherDb, chain->qName)))
     {
     if (chainWinSize < 1000000)
         {
         printf("View ");
         hgcAnchorSomewhere("htcChainAli", item, tdb->track, chain->tName);
         printf("DNA sequence alignment</A> details of parts of chain within browser "
            "window.<BR>\n");
         }
     else
         {
         printf("Zoom so that browser window covers 1,000,000 bases or less "
            "and return here to see alignment details.<BR>\n");
         }
     }
 
@@ -4708,34 +4774,30 @@
     char *hgsid = cartSessionId(cart);
     char *desc = sameString(tdb->table, "altLocations") ? "alternate haplotype" : "fix patch";
     printf("<A HREF=\"hgTracks?hgsid=%s&virtModeType=singleAltHaplo&singleAltHaploId=%s\">"
            "Show this %s placed on its chromosome</A><BR>\n",
            hgsid, itemCpy, desc);
     }
 
 printTrackHtml(tdb);
 freez(&dupe);
 hFreeConn(&conn);
 }
 
 void genericClickHandler(struct trackDb *tdb, char *item, char *itemForUrl)
 /* Put up generic track info */
 {
-#ifdef OLD /* Called now by cartWebStart... */
-jsIncludeFile("jquery.js", NULL);
-jsIncludeFile("utils.js",NULL);
-#endif /* OLD */
 genericClickHandlerPlus(tdb, item, itemForUrl, NULL);
 }
 
 void savePosInTextBox(char *chrom, int start, int end)
 /* Save basic position/database info in text box and hidden var.
    Positions becomes chrom:start-end*/
 {
 char position[128];
 char *newPos;
 snprintf(position, 128, "%s:%d-%d", chrom, start, end);
 newPos = addCommasToPos(database, position);
 cgiMakeTextVar("getDnaPos", newPos, strlen(newPos) + 2);
 cgiContinueHiddenVar("db");
 }
 
@@ -4880,30 +4942,53 @@
     tdb->altColorG = ct->tdb->altColorG;
     tdb->altColorB = ct->tdb->altColorB;
     tdb->useScore = ct->tdb->useScore;
     tdb->private = ct->tdb->private;
     tdb->url = ct->tdb->url;
     tdb->grp = ct->tdb->grp;
     tdb->canPack = ct->tdb->canPack;
     trackDbPolish(tdb);
     slAddHead(&tdbList, tdb);
     }
 
 slReverse(&tdbList);
 return(tdbList);
 }
 
+static struct trackDb *getCustomTrackTdb(char *table)
+/* Find the trackDb structure for a custom track table. */
+{
+struct customTrack *ctList = getCtList();
+struct customTrack *ct = NULL;
+for (ct = ctList; ct != NULL; ct = ct->next)
+    if (sameString(table, ct->tdb->track))
+	return  ct->tdb;
+return NULL;
+}
+
+struct trackDb *getTdbForTrackName(char *trackName)
+/* Given a track name (which may have ct_ or hub_ prepended), tdb */
+{
+assert(trackHash != NULL);
+if (isCustomTrack(trackName))
+    return getCustomTrackTdb(trackName);
+else if (isHubTrack(trackName))
+    return hubConnectAddHubForTrackAndFindTdb( database, trackName, NULL, trackHash);
+else
+    return hashMustFindVal(trackHash, trackName);
+}
+
 
 struct customTrack *lookupCt(char *name)
 /* Return custom track for name, or NULL. */
 {
 struct customTrack *ct;
 
 for (ct=getCtList();  ct != NULL;  ct=ct->next)
     if (sameString(name, ct->tdb->track))
 	return(ct);
 
 return(NULL);
 }
 
 
 void parseSs(char *ss, char **retPslName, char **retFaName, char **retQName)
@@ -6317,60 +6402,64 @@
     printf("BROWSER | SIZE IDENTITY  SCAFFOLD   STRAND    START     END              QUERY      START  END  TOTAL\n");
 printf("-----------------------------------------------------------------------------------------------------\n");
 for (isClicked = 1; isClicked >= 0; isClicked -= 1)
     {
     for (psl = pslList; psl != NULL; psl = psl->next)
 	{
 	if (isPslToPrintByClick(psl, startFirst, isClicked))
 	    {
             char otherString[512];
             char *qName = itemIn;
             if (sameString(itemIn, "PrintAllSequences"))
                 qName = psl->qName;
 	    safef(otherString, sizeof(otherString), "%d&aliTable=%s", psl->tStart, tableName);
             printf("<A HREF=\"%s&db=%s&position=%s%%3A%d-%d\">browser</A> | ",
                    hgTracksPathAndSettings(), database, psl->tName, psl->tStart+1, psl->tEnd);
+	    if (psl->qSize <= MAX_DISPLAY_QUERY_SEQ_SIZE) // Only anchor if small enough 
 		hgcAnchorWindow(hgcCommand, qName, psl->tStart, psl->tEnd, otherString, psl->tName);
-	    printf("%5d  %5.1f%%  %9s     %s %9d %9d  %20s %5d %5d %5d</A>",
+	    printf("%5d  %5.1f%%  %9s     %s %9d %9d  %20s %5d %5d %5d",
 		   psl->match + psl->misMatch + psl->repMatch,
 		   100.0 - pslCalcMilliBad(psl, TRUE) * 0.1,
 		   skipChr(psl->tName), psl->strand, psl->tStart + 1, psl->tEnd,
 		   psl->qName, psl->qStart+1, psl->qEnd, psl->qSize);
+	    if (psl->qSize <= MAX_DISPLAY_QUERY_SEQ_SIZE)
+	        printf("</A>");
 	    printf("\n");
 	    }
 	}
     }
 printf("</TT></PRE>");
 }
 
 void printAlignmentsExtra(struct psl *pslList, int startFirst, char *hgcCommand, char *hgcCommandInWindow,
 		     char *tableName, char *itemIn)
 /* Print list of mRNA alignments with special "in window" alignment function. */
 {
 if (pslList == NULL || tableName == NULL)
     return;
 printAlignmentsSimple(pslList, startFirst, hgcCommand, tableName, itemIn);
 
 struct psl *psl = pslList;
 for (psl = pslList; psl != NULL; psl = psl->next)
     {
     if ( pslTrimToTargetRange(psl, winStart, winEnd) != NULL
         &&
 	!startsWith("xeno", tableName)
 	&& !(startsWith("user", tableName) && pslIsProtein(psl))
 	&& psl->tStart == startFirst
         && sameString(psl->tName, seqName)
+	&& psl->qSize <= MAX_DISPLAY_QUERY_SEQ_SIZE
 	)
 	{
         char otherString[512];
 	safef(otherString, sizeof(otherString), "%d&aliTable=%s",
 	      psl->tStart, tableName);
 	hgcAnchorSomewhere(hgcCommandInWindow, itemIn, otherString, psl->tName);
 	printf("<BR>View details of parts of alignment within browser window</A>.<BR>\n");
 	}
     }
 }
 
 void printAlignments(struct psl *pslList, int startFirst, char *hgcCommand,
 		     char *tableName, char *itemIn)
 /* Print list of mRNA alignments. */
 {
@@ -7727,31 +7816,31 @@
 char startBuf[16], endBuf[16];
 for (bb = bbList; bb != NULL; bb = bb->next)
     {
     bigBedIntervalToRow(bb, seqName, startBuf, endBuf, bedRow, ArraySize(bedRow));
     struct bed *bed = bedLoadN(bedRow, 12);
     if (sameString(bed->name, acc) && (bb->start == start) && (bb->end == end))
 	{
 	bb->next = NULL;
 	break;
 	}
     }
 if (bb == NULL)
     errAbort("item %s not found in range %s:%d-%d in bigBed %s (%s)",
              acc, chrom, start, end, tdb->table, fileName);
 unsigned seqTypeField =  bbExtraFieldIndex(bbi, "seqType");
-psl = pslFromBigPsl(seqName, bb, seqTypeField, &seq, &cdsString);
+psl = getPslAndSeq(tdb, seqName, bb, seqTypeField, &seq, &cdsString);
 if (cdsString)
     genbankParseCds(cdsString,  &cdsStart, &cdsEnd);
 
 
 if (seq == NULL)
     {
     printf("Sequence for %s not available.\n", psl->qName);
     return;
     }
 struct dnaSeq *rnaSeq = newDnaSeq(seq, strlen(seq), acc);
 enum gfType type = gftRna;
 if (pslIsProtein(psl))
     type = gftProt;
 showSomeAlignment(psl, rnaSeq, type, 0, rnaSeq->size, NULL, cdsStart, cdsEnd);
 }
@@ -7788,31 +7877,31 @@
 struct bbiFile *bbi =  bigBedFileOpenAlias(fileName, chromAliasFindAliases);
 struct bigBedInterval *bb, *bbList = bigBedIntervalQuery(bbi, chrom, start, end, 0, lm);
 char *bedRow[32];
 char startBuf[16], endBuf[16];
 for (bb = bbList; bb != NULL; bb = bb->next)
     {
     bigBedIntervalToRow(bb, seqName, startBuf, endBuf, bedRow, ArraySize(bedRow));
     struct bed *bed = bedLoadN(bedRow, 12);
     if (sameString(bed->name, acc))
 	{
 	bb->next = NULL;
 	break;
 	}
     }
 unsigned seqTypeField =  bbExtraFieldIndex(bbi, "seqType");
-wholePsl = pslFromBigPsl(seqName, bb, seqTypeField, &seq, &cdsString);
+wholePsl = getPslAndSeq(tdb, seqName, bb, seqTypeField, &seq, &cdsString);
 
 if (seq == NULL)
     {
     printf("Sequence for %s not available.\n", wholePsl->qName);
     return;
     }
 if (cdsString)
     genbankParseCds(cdsString,  &cdsStart, &cdsEnd);
 
 if (wholePsl->tStart >= winStart && wholePsl->tEnd <= winEnd)
     partPsl = wholePsl;
 else
     partPsl = pslTrimToTargetRange(wholePsl, winStart, winEnd);
 struct dnaSeq *rnaSeq = newDnaSeq(seq, strlen(seq), acc);
 showSomePartialDnaAlignment(partPsl, wholePsl, rnaSeq,
@@ -8061,109 +8150,118 @@
 else
     partPsl = pslTrimToTargetRange(wholePsl, winStart, winEnd);
 
 if (startsWith("xeno", aliTable))
     errAbort("htcCdnaAliInWindow does not support translated alignments.");
 else
     showSomePartialDnaAlignment(partPsl, wholePsl, rnaSeq,
 				NULL, cdsStart, cdsEnd);
 }
 
 void htcChainAli(char *item)
 /* Draw detailed alignment representation of a chain. */
 {
 struct chain *chain;
 struct psl *fatPsl, *psl = NULL;
-int id = atoi(item);
 char *track = cartString(cart, "o");
-char *type = trackTypeInfo(track);
+struct trackDb *tdb = getTdbForTrackName(track);
+char *type = cloneString(tdb->type);
 char *typeWords[2];
 char *otherDb = NULL, *org = NULL, *otherOrg = NULL;
 struct dnaSeq *qSeq = NULL;
 char name[128];
 
-
 /* Figure out other database. */
 if (chopLine(type, typeWords) < ArraySize(typeWords))
     errAbort("type line for %s is short in trackDb", track);
 otherDb = typeWords[1];
 if (! sameWord(otherDb, "seq"))
     {
     otherOrg = hOrganism(otherDb);
     }
 org = hOrganism(database);
 
 /* Load up subset of chain and convert it to part of psl
  * that just fits inside of window. */
-chain = chainLoadIdRange(database, track, seqName, winStart, winEnd, id);
+chain = chainLoadItemInRange(tdb, item);
 if (chain->blockList == NULL)
     {
     printf("None of chain is actually in the window");
     return;
     }
 fatPsl = chainToPsl(chain);
 
 chainFree(&chain);
 
 psl = pslTrimToTargetRange(fatPsl, winStart, winEnd);
 pslFree(&fatPsl);
 
+struct twoBitFile *otherTbf = getOtherTwoBitUrl(tdb);
 if (sameWord(otherDb, "seq"))
     {
     qSeq = hExtSeqPart(database, psl->qName, psl->qStart, psl->qEnd);
     safef(name, sizeof name, "%s", psl->qName);
     }
-else
+else if (otherDb != NULL)
     {
     qSeq = loadGenomePart(otherDb, psl->qName, psl->qStart, psl->qEnd);
     safef(name, sizeof name, "%s.%s", otherOrg, psl->qName);
     }
+else if (otherTbf != NULL)
+    {
+    qSeq = twoBitReadSeqFragLower(otherTbf, psl->qName, psl->qStart, psl->qEnd);
+    safef(name, sizeof name, "%s", psl->qName);
+    }
+if (qSeq == NULL)
+    {
+    errAbort("Can't find query sequence in htcChainAli");
+    }
 char title[1024];
 safef(title, sizeof title, "%s %s vs %s %s ",
        (otherOrg == NULL ? "" : otherOrg), psl->qName, org, psl->tName );
 htmlFramesetStart(title);
 showSomeAlignment(psl, qSeq, gftDnaX, psl->qStart, psl->qEnd, name, 0, 0);
 }
 
 void htcChainTransAli(char *item)
 /* Draw detailed alignment representation of a chain with translated protein */
 {
 struct chain *chain;
 struct psl *fatPsl, *psl = NULL;
-int id = atoi(item);
 char *track = cartString(cart, "o");
 char *type = trackTypeInfo(track);
 char *typeWords[2];
 char *otherDb = NULL, *org = NULL, *otherOrg = NULL;
 struct dnaSeq *qSeq = NULL;
 char name[128];
 int cdsStart = cgiInt("qs");
 int cdsEnd = cgiInt("qe");
 
 /* Figure out other database. */
 if (chopLine(type, typeWords) < ArraySize(typeWords))
     errAbort("type line for %s is short in trackDb", track);
 otherDb = typeWords[1];
 if (! sameWord(otherDb, "seq"))
     {
     otherOrg = hOrganism(otherDb);
     }
 org = hOrganism(database);
 
 /* Load up subset of chain and convert it to part of psl
  * that just fits inside of window. */
-chain = chainLoadIdRange(database, track, seqName, winStart, winEnd, id);
+struct trackDb *tdb = getTdbForTrackName(track);
+chain = chainLoadItemInRange(tdb, item);
 if (chain->blockList == NULL)
     {
     printf("None of chain is actually in the window");
     return;
     }
 fatPsl = chainToPsl(chain);
 
 chainFree(&chain);
 
 psl = pslTrimToTargetRange(fatPsl, winStart, winEnd);
 pslFree(&fatPsl);
 
 if (sameWord(otherDb, "seq"))
     {
     qSeq = hExtSeq(database, psl->qName);
@@ -8877,41 +8975,30 @@
 struct bbiFile *bbi =  bigBedFileOpenAlias(fileName, chromAliasFindAliases);
 struct lm *lm = lmInit(0);
 struct bigBedInterval *bb, *bbList = bigBedIntervalQuery(bbi, seqName, winStart, winEnd, 0, lm);
 struct genePred *gpList = NULL;
 for (bb = bbList; bb != NULL; bb = bb->next)
     {
     struct genePred *gp = (struct genePred *)genePredFromBigGenePred(seqName, bb); 
     if (sameString(gp->name, geneName))
 	slAddHead(&gpList, gp);
     }
 lmCleanup(&lm);
 
 return gpList;
 }
 
-static struct trackDb *getCustomTrackTdb(char *table)
-/* Find the trackDb structure for a custom track table. */
-{
-struct customTrack *ctList = getCtList();
-struct customTrack *ct = NULL;
-for (ct = ctList; ct != NULL; ct = ct->next)
-    if (sameString(table, ct->tdb->track))
-	return  ct->tdb;
-return NULL;
-}
-
 static struct genePred *getGenePredForPosition(char *table, char *geneName)
 /* Build a genePred list for the given table and gene name. */
 {
 struct genePred *gpList = NULL;
 
 if (isCustomTrack(table))
     {
     struct trackDb *tdb = getCustomTrackTdb(table);
     gpList = getGenePredForPositionBigGene(tdb,  geneName);
     }
 else if (isHubTrack(table))
     {
     struct trackDb *tdb = hubConnectAddHubForTrackAndFindTdb( database, table, NULL, trackHash);
     gpList =  getGenePredForPositionBigGene(tdb, geneName);
     }
@@ -8996,31 +9083,31 @@
 int lastChromId = -1;
 char chromName[bbi->chromBpt->keySize+1];
 
 for (bb = bbList; bb != NULL; bb = bb->next)
     {
     bbiCachedChromLookup(bbi, bb->chromId, lastChromId, chromName, sizeof(chromName));
 
     lastChromId=bb->chromId;
     bigBedIntervalToRow(bb, chromName, startBuf, endBuf, bedRow, 4);
 
     // we're assuming that if there are multiple psl's with the same id that
     // they are the same query sequence so we only put out one sequence
     if (sameString(bedRow[3], acc))
 	{
         char *cdsStr, *seq;
-        pslFromBigPsl(chromName, bb, seqTypeField, &seq, &cdsStr);
+	getPslAndSeq(tdb, chromName, bb, seqTypeField, &seq, &cdsStr);
 
         if (translate)
             {
             struct genbankCds cds;
             if (!genbankCdsParse(cdsStr, &cds))
                 errAbort("can't parse CDS for %s: %s", acc, cdsStr);
             int protBufSize = ((cds.end-cds.start)/3)+4;
             char *prot = needMem(protBufSize);
 
             seq[cds.end] = '\0';
             dnaTranslateSome(seq+cds.start, prot, protBufSize);
 
             cartHtmlStart("Protein Translation of query sequence");
             displayProteinPrediction(acc, prot);
             return;
@@ -14738,30 +14825,31 @@
       && strcmp(otherName,"pig")
       && strcmp(otherName,"cat")
       && strcmp(otherName,"dog")
       && strcmp(otherName,"mouse")
       && strcmp(otherName,"rat")
       && strcmp(otherName,"chicken")
       && strcmp(otherName,"fugu")
       && strcmp(otherName,"tetra")
       && strcmp(otherName,"zebrafish")))
     {
     safef( chromStr, sizeof chromStr, "%sChrom" , otherName );
     longXenoPsl1zoo2(tdb, item, otherName, chromStr );
     }
 }
 
+#ifdef OLD
 struct chain *getChainFromRange(char *chainTable, char *chrom, int chromStart, int chromEnd)
 /* get a list of chains for a range */
 {
 char chainTable_chrom[256];
 struct dyString *dy = dyStringNew(128);
 struct chain *chainList = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 safef(chainTable_chrom, 256, "%s_%s",chrom, chainTable);
 
 
 if (hTableExists(database, chainTable_chrom) )
     {
     /* lookup chain if not stored */
     char **row;
     struct sqlResult *sr = NULL;
@@ -14789,30 +14877,31 @@
             qEnd = qSize - qStart;
             qStart = tmp;
             }
         chain = NULL;
         if (chainId != 0)
             {
             chain = chainLoadIdRange(database, chainTable, chrom, chromStart, chromEnd, chainId);
             if (chain != NULL)
                 slAddHead(&chainList, chain);
             }
         }
     sqlFreeResult(&sr);
     }
 return chainList;
 }
+#endif /* OLD */
 
 void htcPseudoGene(char *htcCommand, char *item)
 /* Interface for selecting & displaying alignments from axtInfo
  * for an item from a genePred table. */
 {
 struct genePred *gp = NULL;
 struct axtInfo *aiList = NULL;
 struct axt *axtList = NULL;
 struct sqlResult *sr;
 char **row;
 char trackTemp[256];
 char *track = cartString(cart, "o");
 char *chrom = cartString(cart, "c");
 char *name = cartOptionalString(cart, "i");
 char *db2 = cartString(cart, "db2");