fbfdc0a36bb6d3b0eb57dd07262c059086c7054e braney Fri Jul 31 14:55:15 2026 -0700 hgConvert: link to the alignment request page, behind showLiftRequest, refs #37973 The Convert page's assembly list only offers targets that already have a chain from the source, so it is where a user finds out theirs is missing. Nothing in the tree linked to liftRequest.html, the page that requests a new alignment, so unless someone wrote to us and we sent the URL they were unlikely to find it. Adds "Target assembly not listed? Request an alignment" at the foot of the Destination section, gated by the new hg.conf flag showLiftRequest, which defaults FALSE. Registered in the hg.conf catalog as a release gate. Co-Authored-By: Claude Opus 5 (1M context) diff --git src/hg/hgConvert/hgConvert.c src/hg/hgConvert/hgConvert.c index 55eacd11b4c..fc08a9198da 100644 --- src/hg/hgConvert/hgConvert.c +++ src/hg/hgConvert/hgConvert.c @@ -1,814 +1,825 @@ /* hgConvert - CGI-script to convert browser window coordinates * using chain files */ /* Copyright (C) 2014 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include "common.h" #include "hash.h" #include "errAbort.h" #include "jksql.h" #include "linefile.h" #include "hCommon.h" #include "fa.h" #include "cheapcgi.h" #include "htmshell.h" #include "hdb.h" #include "hui.h" #include "cart.h" #include "web.h" #include "chain.h" #include "liftOver.h" #include "liftOverChain.h" #include "chromInfo.h" #include "net.h" #include "genark.h" #include "trackHub.h" #include "hubConnect.h" #include "quickLift.h" #include "chromAlias.h" #include "jsHelper.h" #include "hPrint.h" +#include "hgConfig.h" #ifdef NOTNOW #include "bigChain.h" #include "bigLink.h" #endif /* CGI Variables */ #define HGLFT_TOORG_VAR "hglft_toOrg" /* TO organism */ #define HGLFT_TODB_VAR "hglft_toDb" /* TO assembly */ #define HGLFT_DO_CONVERT "hglft_doConvert" /* Do the actual conversion */ /* Global Variables */ static struct cart *cart; /* CGI and other variables */ static struct hash *oldVars = NULL; static char *organism = NULL; static char *database = NULL; /* Javascript to support New Assembly pulldown when New Genome changes. */ /* Copies selected values to a hidden form */ char *onChangeToOrg = "document.mainForm.hglft_toDb.value = this.value; document.mainForm.submit();"; static struct dbDb *matchingDb(struct dbDb *list, char *name) /* Find database of given name in list or die trying. */ { struct dbDb *db; for (db = list; db != NULL; db = db->next) { if (sameString(name, db->name)) return db; } struct dbDb *toDb = genarkLiftOverDb(name); if (toDb == NULL) errAbort("Can't find %s in matchingDb", name); return toDb; } static void askForDestination(struct liftOverChain *liftOver, char *fromPos, struct dbDb *fromDb, struct dbDb *toDb) /* set up page for entering data */ { struct dbDb *dbList; boolean askAboutQuickLift = FALSE; boolean quickLift = FALSE; boolean hideOtherTracks = TRUE; if (quickLiftEnabled(cart)) { askAboutQuickLift = TRUE; quickLift = cartUsualBoolean(cart, "doQuickLift", FALSE); hideOtherTracks = cartUsualBoolean(cart, "hideTracksOnConvert", TRUE); } cartWebStart(cart, database, "Convert %s to New Assembly", fromPos); /* Include autocomplete libraries */ jsIncludeAutoCompleteLibs(); /* create HTML form */ puts("
\n"); cartSaveSession(cart); cgiParagraph( "This tool converts the current genome browser position to the corresponding " "region in another assembly or species using genome sequence alignments. " "Select a target assembly below and click Submit to jump to the matching region. " "If a pair of assemblies cannot be selected directly, a sequential conversion " "may still be possible (e.g., mm9 to mm10 to mm39)."); /* CSS for two-section layout */ puts("\n"); puts("
\n"); /* SOURCE SECTION (read-only) */ puts("
\n"); puts("\n"); hPrintf("
Genome: %s
\n", fromDb->organism); hPrintf("
Assembly: %s
\n", fromDb->description); puts("
\n"); /* DESTINATION SECTION (editable) */ puts("
\n"); puts("\n"); /* Hidden fields for form submission */ hPrintf("\n", HGLFT_TOORG_VAR, toDb->organism); hPrintf("\n", HGLFT_TODB_VAR, liftOver->toDb); /* Search bar */ char *searchBarId = "toGenomeSearch"; puts("
\n"); puts("Search:\n"); printGenomeSearchBar(searchBarId, "Search for target genome...", NULL, TRUE, NULL, NULL); puts("
\n"); /* Current selection display */ char *selectedLabel = getCurrentGenomeLabel(liftOver->toDb); hPrintf("
%s
\n", selectedLabel); /* Assembly dropdown (updates based on genome selection) */ puts("
\n"); puts("Assembly:\n"); dbList = hGetLiftOverToDatabases(liftOver->fromDb); printAllAssemblyListHtmlParm(liftOver->toDb, dbList, "hglft_toDbSelect", TRUE, "change", onChangeToOrg); puts("
\n"); /* QuickLift option */ if (askAboutQuickLift) { puts("
\n"); cgiMakeCheckBoxWithId("doQuickLift", quickLift, "doQuickLift"); puts(" \n"); puts("
\n"); puts("
\n"); cgiMakeCheckBoxWithId("hideTracksOnConvert", hideOtherTracks, "hideTracksOnConvert"); puts(" \n"); puts("
\n"); /* The hide option only applies when QuickLift is on; gray it out otherwise. */ jsInline( "function hgcUpdateHideTracks() {\n" " let ql = document.getElementById('doQuickLift');\n" " let hideBox = document.getElementById('hideTracksOnConvert');\n" " let hideRow = document.getElementById('hideTracksOnConvertRow');\n" " if (!ql || !hideBox || !hideRow) return;\n" " hideBox.disabled = !ql.checked;\n" " hideRow.style.opacity = ql.checked ? '1' : '0.5';\n" "}\n" "document.addEventListener('DOMContentLoaded', function() {\n" " let ql = document.getElementById('doQuickLift');\n" " if (ql) ql.addEventListener('change', hgcUpdateHideTracks);\n" " hgcUpdateHideTracks();\n" "});\n"); } +/* The assembly list only offers targets that already have a chain from this source, so + * this is where a user finds out theirs is missing. Point them at the request page. */ +if (cfgOptionBooleanDefault("showLiftRequest", FALSE)) + { + puts("
\n"); + puts("Target assembly not listed? " + "Request an alignment\n"); + puts("
\n"); + } + puts("
\n"); /* end destination section */ puts("
\n"); /* end grid */ /* Submit button centered below */ puts("
\n"); cgiMakeButton(HGLFT_DO_CONVERT, "Submit"); puts("
\n"); /* JavaScript initialization for autocomplete with liftOver filtering */ jsInlineF( "let validTargets = new Set();\n" "\n" "fetch('../cgi-bin/hubApi/liftOver/listExisting?fromGenome=%s')\n" " .then(response => response.json())\n" " .then(data => {\n" " if (data.existingLiftOvers) {\n" " data.existingLiftOvers.forEach(chain => validTargets.add(chain.toDb));\n" " }\n" "\n" " // Mark recent/popular genomes that don't have a liftOver from source as disabled\n" " function markInvalidTargets(items) {\n" " let sourceDb = '%s';\n" " return items.filter(function(item) {\n" " return (item.db || item.genome) !== sourceDb;\n" " }).map(function(item) {\n" " if (!validTargets.has(item.db || item.genome)) {\n" " item.disabled = true;\n" " item.disabledReason = 'No liftOver available from source assembly';\n" " }\n" " return item;\n" " });\n" " }\n" "\n" " // Custom onServerReply that processes results and filters to valid targets\n" " function processAndFilterResults(result, term) {\n" " let processed = processFindGenome(result, term);\n" " let filtered = processed.filter(item => validTargets.has(item.genome));\n" " if (filtered.length === 0 && processed.length > 0) {\n" " // Found genomes but none have liftOver from source\n" " return [{label: 'No liftOver available for matching genomes', value: '', genome: '', disabled: true}];\n" " } else if (filtered.length === 0) {\n" " // No genomes matched the search at all\n" " return [{label: 'No genomes found', value: '', genome: '', disabled: true}];\n" " }\n" " return filtered;\n" " }\n" "\n" " // Error handler for API failures (e.g. HTTP 400)\n" " function onSearchError(jqXHR, textStatus, errorThrown, term) {\n" " return [{label: 'No genomes found', value: '', genome: '', disabled: true}];\n" " }\n" "\n" " function onGenomeSelect(selectEle, item) {\n" " // Ignore disabled/placeholder items\n" " if (item.disabled || !item.genome) {\n" " return;\n" " }\n" " selectEle.textContent = item.label;\n" " document.mainForm.%s.value = item.commonName.split('(')[0].trim();\n" " document.mainForm.%s.value = item.genome;\n" " document.mainForm.submit();\n" " }\n" "\n" " let selectEle = document.getElementById('toGenomeLabel');\n" " initSpeciesAutoCompleteDropdown('%s', onGenomeSelect.bind(null, selectEle), null, null, processAndFilterResults, onSearchError, markInvalidTargets);\n" " });\n" "\n" "document.addEventListener('DOMContentLoaded', () => {\n" " let btn = document.getElementById('%sButton');\n" " if (btn) {\n" " btn.addEventListener('click', () => {\n" " let val = document.getElementById('%s').value;\n" " $('[id=\\x27%s\\x27]').autocompleteCat('search', val);\n" " });\n" " }\n" " let toggle = document.getElementById('%sToggle');\n" " if (toggle) {\n" " let wasOpen = false;\n" " toggle.addEventListener('mousedown', () => {\n" " let $input = $('[id=\\x27%s\\x27]');\n" " wasOpen = $input.autocompleteCat('widget').is(':visible');\n" " });\n" " toggle.addEventListener('click', () => {\n" " let $input = $('[id=\\x27%s\\x27]');\n" " if (wasOpen) {\n" " $input.autocompleteCat('close');\n" " } else {\n" " $input.val('');\n" " $input.autocompleteCat('search', '');\n" " $input.focus();\n" " }\n" " });\n" " }\n" "});\n" , liftOver->fromDb , liftOver->fromDb , HGLFT_TOORG_VAR , HGLFT_TODB_VAR , searchBarId, searchBarId, searchBarId, searchBarId , searchBarId, searchBarId, searchBarId ); puts("
\n"); webNewSection("Notes"); cgiParagraph( "QuickLift tracks: When enabled, QuickLift displays tracks from " "the source assembly mapped onto the target assembly, allowing you to view " "your current tracks in the context of the new genome. For more information, see the " "QuickLift documentation."); cgiParagraph( "Hide all default tracks on the target: When QuickLift is enabled, this " "option (on by default) hides the target assembly's default tracks so that only " "your QuickLifted tracks are shown, letting you focus on the tracks carried over " "from the source assembly. Uncheck it to display the target assembly's default " "tracks alongside the QuickLifted tracks."); cgiParagraph( "If your desired target assembly is not available, you can search for it " "and request it on our " "Assembly Search page."); cartWebEnd(); } static double scoreLiftOverChain(struct liftOverChain *chain, char *fromOrg, char *fromDb, char *toOrg, char *toDb, struct hash *dbRank ) /* Score the chain in terms of best match for cart settings */ { double score = 0; char *chainFromOrg = hOrganism(chain->fromDb); char *chainToOrg = hOrganism(chain->toDb); int fromRank = hashIntValDefault(dbRank, chain->fromDb, 0); int maxRank = hashIntVal(dbRank, "maxRank"); int toRank = hashIntValDefault(dbRank, chain->toDb, maxRank); if (sameOk(fromOrg,chainFromOrg) && sameOk(fromDb,chain->fromDb) && sameOk(toOrg,chainToOrg) && sameOk(toDb,chain->toDb)) score += 10000000; if (sameOk(fromOrg,chainFromOrg)) score += 2000000; if (sameOk(fromDb,chain->fromDb)) score += 1000000; if (sameOk(toOrg,chainToOrg)) score += 200000; if (sameOk(toDb,chain->toDb)) score += 100000; // at the moment we aren't ranking genark db's //if (toRank == 0) // chains to db's that are not active shouldn't be considered //return 0; score += 10*(maxRank-fromRank); score += (maxRank - toRank); return score; } static struct liftOverChain *defaultChoices(struct liftOverChain *chainList, char *fromOrg, char *fromDb) /* Out of a list of liftOverChains and a cart, choose a * list to display. */ { char *toOrg, *toDb; struct liftOverChain *choice = NULL; struct hash *dbRank = hGetDatabaseRank(); double bestScore = -1; struct liftOverChain *this = NULL; /* Get the initial values. */ toOrg = cartCgiUsualString(cart, HGLFT_TOORG_VAR, "0"); toDb = cartCgiUsualString(cart, HGLFT_TODB_VAR, "0"); if (sameWord(toOrg,"0")) toOrg = NULL; if (sameWord(toDb,"0")) toDb = NULL; /* If toDb was explicitly set, find exact match in chain list */ if (toDb != NULL) { for (this = chainList; this != NULL; this = this->next) if (sameString(toDb, this->toDb)) return this; } if (toOrg == NULL) toOrg = "Human"; for (this = chainList; this != NULL; this = this->next) { double score = scoreLiftOverChain(this, fromOrg, fromDb, toOrg, toDb, dbRank); if (score > bestScore) { choice = this; bestScore = score; } } return choice; } static char *skipWord(char *s) /* Skip word, and any leading spaces before next word. */ { return skipLeadingSpaces(skipToSpaces(s)); } static long chainTotalBlockSize(struct chain *chain) /* Return sum of sizes of all blocks in chain */ { struct cBlock *block; long total = 0; for (block = chain->blockList; block != NULL; block = block->next) total += block->tEnd - block->tStart; return total; } static struct chain *chainLoadIntersecting(char *fileName, char *chrom, int start, int end) /* Load the chains that intersect given region. */ { struct lineFile *lf = netLineFileOpen(fileName); char *line; int chromNameSize = strlen(chrom); struct chain *chainList = NULL, *chain; #ifdef SOON /* Put in if we index. */ boolean gotChrom = FALSE; #endif /* SOON */ int chainCount = 0; while (lineFileNextReal(lf, &line)) { if (startsWith("chain", line) && isspace(line[5])) { ++chainCount; line = skipWord(line); /* Skip over 'chain' */ line = skipWord(line); /* Skip over chain score */ if (startsWith(chrom, line) && isspace(line[chromNameSize])) { #ifdef SOON /* Put in if we index. */ gotChrom = TRUE; #endif /* SOON */ lineFileReuse(lf); chain = chainReadChainLine(lf); if (rangeIntersection(chain->tStart, chain->tEnd, start, end) > 0) { chainReadBlocks(lf, chain); slAddHead(&chainList, chain); } else chainFree(&chain); } #ifdef SOON /* Put in if we index. */ else if (gotChrom) break; /* We assume file is sorted by chromosome, so we're done. */ #endif /* SOON */ } } lineFileClose(&lf); slReverse(&chainList); return chainList; } static struct chain *chainLoadAndTrimIntersecting(char *fileName, char *chrom, int start, int end) /* Load the chains that intersect given region, and trim them * to fit region. */ { struct chain *rawList, *chainList = NULL, *chain, *next; rawList = chainLoadIntersecting(fileName, chrom, start, end); for (chain = rawList; chain != NULL; chain = next) { struct chain *subChain, *chainToFree; next = chain->next; chainSubsetOnT(chain, start, end, &subChain, &chainToFree); if (subChain != NULL) slAddHead(&chainList, subChain); if (chainToFree != NULL) chainFree(&chain); } slSort(&chainList, chainCmpScore); return chainList; } #ifdef NOTNOW struct segment { struct segment *next; unsigned start, end; char strand; }; void compressSegs(struct segment *segs) { struct segment *nextSeg = NULL; struct segment *growSeg = NULL; for(; segs; segs = nextSeg) { nextSeg = segs->next; if (nextSeg == NULL) break; if (growSeg == NULL) growSeg = segs; if ((nextSeg->start < growSeg->end + 10000) || (nextSeg->strand != growSeg->strand)) { growSeg->end = nextSeg->end; //nextSeg = nextSeg->next; } else { growSeg->next = segs; growSeg = NULL; } } } static void getSegs(char *fromDb, char *toDb, struct segment **fromSegs, struct segment **toSegs, char *chrom, unsigned seqStart, unsigned seqEnd) { char *quickLiftFile = quickLiftGetChainPath(cart, fromDb, toDb); struct lm *lm = lmInit(0); //struct bigBedInterval *bbChain, *bbChainList = bigBedIntervalQuery(bbiChain, chrom, seqStart, seqEnd, 0, lm); struct bbiFile *bbiChain = bigBedFileOpenAlias(quickLiftFile, chromAliasFindAliases); struct bigBedInterval *bbChain, *bbChainList = bigBedIntervalQuery(bbiChain, chrom, seqStart, seqEnd, 0, lm); char *links = bigChainGetLinkFile(quickLiftFile); struct bbiFile *bbiLink = bigBedFileOpenAlias(links, chromAliasFindAliases); struct bigBedInterval *bbLink, *bbLinkList = bigBedIntervalQuery(bbiLink, chrom, seqStart, seqEnd, 0, lm); char *chainRow[1024]; char *linkRow[1024]; char startBuf[16], endBuf[16]; bigBedIntervalToRow(bbChainList, chrom, startBuf, endBuf, chainRow, ArraySize(chainRow)); struct bigChain *fbc = bigChainLoad(chainRow); for (bbChain = bbChainList; bbChain != NULL; bbChain = bbChain->next) { bigBedIntervalToRow(bbChain, chrom, startBuf, endBuf, chainRow, ArraySize(chainRow)); struct bigChain *bc = bigChainLoad(chainRow); if (differentString(bc->chrom, fbc->chrom)) continue; for (bbLink = bbLinkList; bbLink != NULL; bbLink = bbLink->next) { bigBedIntervalToRow(bbLink, chrom, startBuf, endBuf, linkRow, ArraySize(linkRow)); struct bigLink *bl = bigLinkLoad(linkRow); if (!sameString(bl->name, bc->name)) continue; boolean isFlipped = fbc->strand[0] != bc->strand[0]; struct segment *seg; AllocVar(seg); seg->start = bl->chromStart; seg->end = bl->chromEnd; seg->strand = 0; slAddHead(fromSegs, seg); unsigned width = bl->chromEnd - bl->chromStart; unsigned qStart = bl->qStart; unsigned qEnd = bl->qStart + width; if (bc->strand[0] == '-') { qStart = bc->qSize - qStart; qEnd = bc->qSize - qEnd; } AllocVar(seg); seg->start = qStart; seg->end = qEnd; seg->strand = isFlipped; slAddHead(toSegs, seg); } } slReverse(toSegs); //compressSegs(*toSegs); slReverse(fromSegs); //compressSegs(*fromSegs); } static void printSegs(struct segment *segs) { unsigned segNum = 0; jsInline("segments: [\n"); for(; segs; segs = segs->next) jsInlineF("{ name: '%d', start: %d, end: %d },\n", segNum++, segs->start, segs->end); jsInline("]\n"); } static void drawSegments(char *fromDb, char *toDb, struct chain *chain) { struct segment *fromSegs = NULL; struct segment *toSegs = NULL; /* unsigned seqStart = chain->tStart; unsigned seqEnd = chain->tEnd; char *chrom = chain->tName; */ unsigned qStart = chain->qStart; unsigned qEnd = chain->qEnd; if (chain->qStrand == '-') { unsigned saveQStart = qStart; qStart = chain->qSize - qEnd; qEnd = chain->qSize - saveQStart; } getSegs(fromDb, toDb, &fromSegs, &toSegs, chain->qName, chain->qStart, chain->qEnd); printf(""); jsIncludeFile("parallelSegments.js", NULL); jsInline("function drawCustom() {\n"); jsInline("const line1Data = {\n"); jsInlineF("range: { start: %d, end: %d },\n", chain->tStart, chain->tEnd); jsInlineF("label: \"%s %s:%d-%d\",\n", fromDb, chain->tName, chain->tStart, chain->tEnd); printSegs(toSegs); jsInline("}\n"); jsInline("const line2Data = {\n"); jsInlineF("range: { start: %d, end: %d },\n", qStart, qEnd); jsInlineF("label: \"%s %s:%d-%d\",\n", fromDb, chain->qName, qStart, qEnd); printSegs(fromSegs); jsInline("}\n"); jsInline("ParallelSegments.drawCoordinateBasedSegments(line1Data, line2Data);\n"); jsInline("}\n"); jsInline("drawCustom();\n"); } #endif static void doConvert(char *fromPos) /* Actually do the conversion */ { struct dbDb *fromDb = hDbDb(trackHubSkipHubName(database)), *toDb = hDbDb(cartString(cart, HGLFT_TODB_VAR)); #ifdef NOTNOW boolean doSegments = TRUE; #endif if (fromDb == NULL) { char buffer[4096]; safef(buffer, sizeof buffer, "'%s'", trackHubSkipHubName(database)); fromDb = genarkLiftOverDbs(buffer); } if (toDb == NULL) toDb = genarkLiftOverDb(cartString(cart, HGLFT_TODB_VAR)); if (!fromDb || !toDb) errAbort("Early error - unable to find matching database records in dbDb - please contact support"); chromAliasSetup(database); cartWebStart(cart, database, "%s %s %s to %s %s", fromDb->organism, fromDb->description, fromPos, toDb->organism, toDb->description); char *fileName = liftOverChainFile(trackHubSkipHubName(fromDb->name), trackHubSkipHubName(toDb->name)); if (isEmpty(fileName)) errAbort("Unable to find a chain file from %s to %s - please contact support", fromDb->name, toDb->name); fileName = hReplaceGbdbMustDownload(fileName); char *chrom; int start, end; int origSize; struct chain *chainList, *chain; struct dyString *visDy = NULL; if (!hgParseChromRange(database, fromPos, &chrom, &start, &end)) errAbort("position %s is not in chrom:start-end format", fromPos); origSize = end - start; boolean doQuickLift = cartUsualBoolean(cart, "doQuickLift", FALSE); cartRemove(cart, "doQuickLift"); boolean hideOtherTracks = cartUsualBoolean(cart, "hideTracksOnConvert", TRUE); cartRemove(cart, "hideTracksOnConvert"); unsigned quickChain = 0; unsigned quickHub = 0; struct trackDb *badList = NULL; if (doQuickLift) { quickChain = quickLiftGetChainId(cart, trackHubSkipHubName(fromDb->name), trackHubSkipHubName(toDb->name)); if (quickChain == 0) errAbort("can't find quickChain from %s to %s", fromDb->name, toDb->name); visDy = newDyString(1024); char *newHub = trackHubBuild(fromDb->name, cart, visDy, &badList); char *error = NULL; quickHub = hubFindOrAddUrlInStatusTable(cart, newHub, &error); if (error != NULL) errAbort("can't add quickLift hub (error %s)",error); } chainList = chainLoadAndTrimIntersecting(fileName, chrom, start, end); if (chainList == NULL) printf("Sorry this position couldn't be found in new assembly"); else { for (chain = chainList; chain != NULL; chain = chain->next) { int blockSize; int qStart, qEnd; if (chain->qStrand == '-') { qStart = chain->qSize - chain->qEnd; qEnd = chain->qSize - chain->qStart; } else { qStart = chain->qStart; qEnd = chain->qEnd; } blockSize = chainTotalBlockSize(chain); /* Check if the toDb database exists and if the chromosome sequence file (of the hgConvert result) exists in the location specified in chromInfo for the toDb. */ boolean chromSeqExists = (sqlDatabaseExists(toDb->name) && chromSeqFileExists(toDb->name, chain->qName)); /* Check if the toDb has active set to 1 in dbDb if the toDb database exists. If these conditions are met then print position link to browser for toDb, otherwise just print position without link. */ boolean startedAnchor = FALSE; visDy = newDyString(20); if ((hDbIsActive(toDb->name) && chromSeqExists) || startsWith("hub:",toDb->nibPath) || sameString(toDb->nibPath, "genark")) { if (quickChain) printf("", hgTracksName(), toDb->name, chain->qName, qStart+1, qEnd, quickHub, toDb->name, quickChain, hideOtherTracks ? "&hideTracks=on" : ""); else printf("", hgTracksName(), toDb->name, chain->qName, qStart+1, qEnd); startedAnchor = TRUE; } printf("%s:%d-%d", chain->qName, qStart+1, qEnd); if (startedAnchor) printf(""); printf(" (%3.1f%% of bases, %3.1f%% of span)
\n", 100.0 * blockSize/origSize, 100.0 * (chain->tEnd - chain->tStart) / origSize); #ifdef NTONOW if (doSegments) drawSegments(fromDb->name, toDb->name, chain); break; #endif } } if (badList) { printf("
Some of your tracks failed to lift because the type is not supported by QuickLift.

"); printf(""); for(; badList; badList = badList->next) { printf("", badList->shortLabel, badList->type); } printf("
Short labelType
%s%s
"); } cartWebEnd(); } static struct liftOverChain *cleanLiftOverList(struct liftOverChain *list) /* eliminate from the list where toDb doesn't exist in dbDb */ { struct liftOverChain *cleanList = NULL; struct hash *dbDbHash = hDbDbHash(); struct liftOverChain *this = NULL; struct liftOverChain *next = NULL; for (this = list; this != NULL; this = next) { next = this->next; if (hashLookup(dbDbHash, this->toDb) || startsWith("GC", this->toDb)) slAddHead(&cleanList, this); else liftOverChainFree(&this); } slReverse(&cleanList); return cleanList; } static void doMiddle(struct cart *theCart) /* Set up globals and make web page */ { cart = theCart; getDbAndGenome(cart, &database, &organism, oldVars); // Bots sometimes hit hgConvert with a db but no position in the cart; fall // back to the assembly's default position rather than aborting. char *fromPos = cartUsualString(cart, "position", hDefaultPos(database)); // Try to deal with virt chrom position used by hgTracks. if (startsWith( MULTI_REGION_CHROM, cartUsualString(cart, "position", "")) || startsWith(OLD_MULTI_REGION_CHROM, cartUsualString(cart, "position", ""))) cartSetString(cart, "position", cartUsualString(cart, "nonVirtPosition", "")); if (cartVarExists(cart, HGLFT_DO_CONVERT)) doConvert(fromPos); else { struct liftOverChain *checkLiftOverList = liftOverChainListForDbFiltered(trackHubSkipHubName(database)); struct liftOverChain *liftOverList = cleanLiftOverList(checkLiftOverList); struct liftOverChain *choice = defaultChoices(liftOverList, organism, trackHubSkipHubName(database)); if (choice == NULL) errAbort("Sorry, no conversions available from this assembly."); struct dbDb *dbList, *fromDb, *toDb; dbList = hDbDbListMaybeCheck(FALSE); fromDb = matchingDb(dbList, choice->fromDb); toDb = matchingDb(dbList, choice->toDb); askForDestination(choice, fromPos, fromDb, toDb); liftOverChainFreeList(&liftOverList); } } /* Null terminated list of CGI Variables we don't want to save * permanently. */ char *excludeVars[] = { "submit", HGLFT_DO_CONVERT, NULL}; int main(int argc, char *argv[]) /* Process command line. */ { long enteredMainTime = clock1000(); oldVars = hashNew(10); cgiSpoof(&argc, argv); cartEmptyShell(doMiddle, hUserCookie(), excludeVars, oldVars); cgiExitTime("hgConvert", enteredMainTime); return 0; }