54c94c0537be6079976376847328793b95c1a2b5
lrnassar
Mon Aug 19 14:50:46 2019 -0700
Removed excess
anchor from CR refs #24017
diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c
index de53c8b..e52ea25 100644
--- src/hg/hgTracks/hgTracks.c
+++ src/hg/hgTracks/hgTracks.c
@@ -1,10310 +1,10310 @@
/* hgTracks - the /riginal, and still the largest module for the UCSC Human Genome
* Browser main cgi script. Currently contains most of the track framework, though
* there's quite a bit of other framework type code in simpleTracks.c. The main
* routine got moved to create a new entry point to the bulk of the code for the
* hgRenderTracks web service. See mainMain.c for the main used by the hgTracks CGI. */
/* Copyright (C) 2014 The Regents of the University of California
* See README in this or parent directory for licensing information. */
#include
#include
#include "common.h"
#include "hCommon.h"
#include "linefile.h"
#include "portable.h"
#include "memalloc.h"
#include "localmem.h"
#include "obscure.h"
#include "dystring.h"
#include "hash.h"
#include "jksql.h"
#include "gfxPoly.h"
#include "memgfx.h"
#include "hvGfx.h"
#include "psGfx.h"
#include "cheapcgi.h"
#include "hPrint.h"
#include "htmshell.h"
#include "cart.h"
#include "hdb.h"
#include "hui.h"
#include "hgFind.h"
#include "hgTracks.h"
#include "trashDir.h"
#include "grp.h"
#include "versionInfo.h"
#include "web.h"
#include "cds.h"
#include "cutterTrack.h"
#include "wikiTrack.h"
#include "ctgPos.h"
#include "bed.h"
#include "bigBed.h"
#include "bigWig.h"
#include "bedCart.h"
#include "udc.h"
#include "customTrack.h"
#include "trackHub.h"
#include "hubConnect.h"
#include "cytoBand.h"
#include "ensFace.h"
#include "pcrResult.h"
#include "jsHelper.h"
#include "mafTrack.h"
#include "hgConfig.h"
#include "encode.h"
#include "agpFrag.h"
#include "imageV2.h"
#include "suggest.h"
#include "search.h"
#include "errCatch.h"
#include "iupac.h"
#include "botDelay.h"
#include "chromInfo.h"
#include "extTools.h"
#include "basicBed.h"
#include "customFactory.h"
#include "genbank.h"
#include "bigWarn.h"
#include "wigCommon.h"
#include "knetUdc.h"
#include "hex.h"
#include
#include "customComposite.h"
/* Other than submit and Submit all these vars should start with hgt.
* to avoid weeding things out of other program's namespaces.
* Because the browser is a central program, most of its cart
* variables are not hgt. qualified. It's a good idea if other
* program's unique variables be qualified with a prefix though. */
char *excludeVars[] = { "submit", "Submit", "dirty", "hgt.reset",
"hgt.in1", "hgt.in2", "hgt.in3", "hgt.inBase",
"hgt.out1", "hgt.out2", "hgt.out3", "hgt.out4",
"hgt.left1", "hgt.left2", "hgt.left3",
"hgt.right1", "hgt.right2", "hgt.right3",
"hgt.dinkLL", "hgt.dinkLR", "hgt.dinkRL", "hgt.dinkRR",
"hgt.tui", "hgt.hideAll", "hgt.visAllFromCt",
"hgt.psOutput", "hideControls", "hgt.toggleRevCmplDisp",
"hgt.collapseGroups", "hgt.expandGroups", "hgt.suggest",
"hgt.jump", "hgt.refresh", "hgt.setWidth",
"hgt.trackImgOnly", "hgt.ideogramToo", "hgt.trackNameFilter", "hgt.imageV1", "hgt.suggestTrack", "hgt.setWidth",
TRACK_SEARCH, TRACK_SEARCH_ADD_ROW, TRACK_SEARCH_DEL_ROW, TRACK_SEARCH_PAGER,
"hgt.contentType", "hgt.positionInput", "hgt.internal",
"sortExp", "sortSim", "hideTracks", "ignoreCookie",
NULL };
/* These variables persist from one incarnation of this program to the
* next - living mostly in the cart. */
boolean baseShowPos; /* TRUE if should display full position at top of base track */
boolean baseShowAsm; /* TRUE if should display assembly info at top of base track */
boolean baseShowScaleBar; /* TRUE if should display scale bar at very top of base track */
boolean baseShowRuler; /* TRUE if should display the basic ruler in the base track (default) */
char *baseTitle = NULL; /* Title it should display top of base track (optional)*/
static char *userSeqString = NULL; /* User sequence .fa/.psl file. */
/* These variables are set by getPositionFromCustomTracks() at the very
* beginning of tracksDisplay(), and then used by loadCustomTracks(). */
char *ctFileName = NULL; /* Custom track file. */
struct customTrack *ctList = NULL; /* Custom tracks. */
boolean hasCustomTracks = FALSE; /* whether any custom tracks are for this db*/
struct slName *browserLines = NULL; /* Custom track "browser" lines. */
boolean withNextItemArrows = FALSE; /* Display next feature (gene) navigation buttons */
boolean withPriorityOverride = FALSE; /* Display priority for each track to allow reordering */
int gfxBorder = hgDefaultGfxBorder; /* Width of graphics border. */
int guidelineSpacing = 12; /* Pixels between guidelines. */
boolean withIdeogram = TRUE; /* Display chromosome ideogram? */
int rulerMode = tvHide; /* on, off, full */
struct hvGfx *hvgSide = NULL; // Extra pointer to a sideLabel image that can be built if needed
char *rulerMenu[] =
/* dropdown for ruler visibility */
{
"hide",
"dense",
"full"
};
char *protDbName; /* Name of proteome database for this genome. */
#define MAX_CONTROL_COLUMNS 6
#define LOW 1
#define MEDIUM 2
#define BRIGHT 3
#define MAXCHAINS 50000000
boolean hgDebug = FALSE; /* Activate debugging code. Set to true by hgDebug=on in command line*/
int imagePixelHeight = 0;
struct hash *oldVars = NULL;
struct jsonElement *jsonForClient = NULL;
boolean hideControls = FALSE; /* Hide all controls? */
boolean trackImgOnly = FALSE; /* caller wants just the track image and track table html */
boolean ideogramToo = FALSE; /* caller wants the ideoGram (when requesting just one track) */
/* Structure returned from resolvePosition.
* We use this to to expand any tracks to full
* that were found to contain the searched-upon
* position string */
struct hgPositions *hgp = NULL;
/* Other global variables. */
struct group *groupList = NULL; /* List of all tracks. */
char *browserName; /* Test, preview, or public browser */
char *organization; /* UCSC */
struct hash *trackHash = NULL; /* Hash of the tracks by their name. */
#ifdef DEBUG
void uglySnoopTrackList(int depth, struct track *trackList)
/* Print out some info on track list. */
{
struct track *track;
for (track = trackList; track != NULL; track = track->next)
{
if (stringIn("FaireH1h", track->track))
{
repeatCharOut(uglyOut, '+', depth);
}
uglySnoopTrackList(depth+1, track->subtracks);
}
}
#endif /* DEBUG */
struct track *trackFindByName(struct track *tracks, char *trackName)
/* find a track in tracks by name, recursively searching subtracks */
{
struct track *track;
for (track = tracks; track != NULL; track = track->next)
{
if (sameString(track->track, trackName))
return track;
else if (track->subtracks != NULL)
{
struct track *st = trackFindByName(track->subtracks, trackName);
if (st != NULL)
return st;
}
}
return NULL;
}
int tgCmpPriority(const void *va, const void *vb)
/* Compare to sort based on priority; use shortLabel as secondary sort key. */
{
const struct track *a = *((struct track **)va);
const struct track *b = *((struct track **)vb);
float dif = 0;
if (a->group && b->group)
dif = a->group->priority - b->group->priority;
if (dif == 0)
dif = a->priority - b->priority;
if (dif < 0)
return -1;
else if (dif == 0.0)
/* secondary sort on label */
return strcasecmp(a->shortLabel, b->shortLabel);
else
return 1;
}
int trackRefCmpPriority(const void *va, const void *vb)
/* Compare based on priority. */
{
const struct trackRef *a = *((struct trackRef **)va);
const struct trackRef *b = *((struct trackRef **)vb);
return tgCmpPriority(&a->track, &b->track);
}
int gCmpPriority(const void *va, const void *vb)
/* Compare groups based on priority. */
{
const struct group *a = *((struct group **)va);
const struct group *b = *((struct group **)vb);
float dif = a->priority - b->priority;
if (dif == 0)
return 0;
if (dif < 0)
return -1;
else if (dif == 0.0)
return 0;
else
return 1;
}
void changeTrackVis(struct group *groupList, char *groupTarget, int changeVis)
/* Change track visibilities. If groupTarget is
* NULL then set visibility for tracks in all groups. Otherwise,
* just set it for the given group. If vis is -2, then visibility is
* unchanged. If -1 then set visibility to default, otherwise it should
* be tvHide, tvDense, etc.
* If we are going back to default visibility, then reset the track
* ordering also. */
{
struct group *group;
if (changeVis == -2)
return;
for (group = groupList; group != NULL; group = group->next)
{
struct trackRef *tr;
if (groupTarget == NULL || sameString(group->name,groupTarget))
{
static char pname[512];
/* if default vis then reset group priority */
if (changeVis == -1)
group->priority = group->defaultPriority;
for (tr = group->trackList; tr != NULL; tr = tr->next)
{
struct track *track = tr->track;
struct trackDb *tdb = track->tdb;
if (changeVis == -1) // to default
{
if (tdbIsComposite(tdb))
{
safef(pname, sizeof(pname),"%s.*",track->track); //to remove all settings associated with this composite!
cartRemoveLike(cart,pname);
struct track *subTrack;
for (subTrack = track->subtracks;subTrack != NULL; subTrack = subTrack->next)
{
subTrack->visibility = tdb->visibility;
cartRemove(cart, subTrack->track);
}
}
/* restore defaults */
if (tdbIsSuperTrackChild(tdb) || tdbIsCompositeChild(tdb))
{
assert(tdb->parent != NULL && tdb->parent->track);
cartRemove(cart, tdb->parent->track);
if (withPriorityOverride)
{
safef(pname, sizeof(pname), "%s.priority",tdb->parent->track);
cartRemove(cart, pname);
}
}
track->visibility = tdb->visibility;
cartRemove(cart, track->track);
/* set the track priority back to the default value */
if (withPriorityOverride)
{
safef(pname, sizeof(pname), "%s.priority",track->track);
cartRemove(cart, pname);
track->priority = track->defaultPriority;
}
}
else // to changeVis value (Usually tvHide)
{
/* change to specified visibility */
if (tdbIsSuperTrackChild(tdb))
{
assert(tdb->parent != NULL);
/* Leave supertrack members alone -- only change parent */
struct trackDb *parentTdb = tdb->parent;
if ((changeVis == tvHide && !parentTdb->isShow) ||
(changeVis != tvHide && parentTdb->isShow))
{
/* remove if setting to default vis */
cartRemove(cart, parentTdb->track);
}
else
cartSetString(cart, parentTdb->track,
changeVis == tvHide ? "hide" : "show");
}
else // Not super child
{
if (changeVis == tdb->visibility)
/* remove if setting to default vis */
cartRemove(cart, track->track);
else
cartSetString(cart, track->track, hStringFromTv(changeVis));
track->visibility = changeVis;
}
// Whether super child or not, if its a composite, then handle the children
if (tdbIsComposite(tdb))
{
struct track *subtrack;
for (subtrack=track->subtracks;subtrack!=NULL;subtrack=subtrack->next)
{
if (changeVis == tvHide) // Since subtrack level vis is an
cartRemove(cart, subtrack->track); // override, simply remove to hide
else
cartSetString(cart, subtrack->track, hStringFromTv(changeVis));
subtrack->visibility = changeVis;
}
}
}
}
}
}
slSort(&groupList, gCmpPriority);
}
int trackOffsetX()
/* Return x offset where track display proper begins. */
{
int x = gfxBorder;
if (withLeftLabels)
x += tl.leftLabelWidth + gfxBorder;
return x;
}
static void mapBoxTrackUi(struct hvGfx *hvg, int x, int y, int width,
int height, char *name, char *shortLabel, char *id)
/* Print out image map rectangle that invokes hgTrackUi. */
{
x = hvGfxAdjXW(hvg, x, width);
char *url = trackUrl(name, chromName);
if (theImgBox && curImgTrack)
{
struct imgSlice *curSlice = imgTrackSliceGetByType(curImgTrack,stButton);
if (curSlice)
sliceAddLink(curSlice,url,shortLabel);
}
else
{
hPrintf("\n");
}
freeMem(url);
}
static void mapBoxToggleComplement(struct hvGfx *hvg, int x, int y, int width, int height,
struct track *toggleGroup, char *chrom,
int start, int end, char *message)
/*print out a box along the DNA bases that toggles a cart variable
* "complement" to complement the DNA bases at the top by the ruler*/
{
struct dyString *ui = uiStateUrlPart(toggleGroup);
x = hvGfxAdjXW(hvg, x, width);
if (theImgBox && curImgTrack)
{
char link[512];
safef(link,sizeof(link),"%s?complement_%s=%d&%s", hgTracksName(), database,
!cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE),ui->string);
imgTrackAddMapItem(curImgTrack,link,(char *)(message != NULL?message:NULL),x, y, x+width, y+height, NULL);
}
else
{
hPrintf("string);
freeDyString(&ui);
if (message != NULL)
mapStatusMessage("%s", message);
hPrintf(">\n");
}
}
char *trackUrl(char *mapName, char *chromName)
/* Return hgTrackUi url; chromName is optional. */
{
char *encodedMapName = cgiEncode(mapName);
char buf[2048];
char *hgTrackUi = hTrackUiForTrack(mapName);
if(chromName == NULL)
safef(buf, sizeof(buf), "%s?%s=%s&g=%s", hgTrackUi, cartSessionVarName(), cartSessionId(cart), encodedMapName);
else
safef(buf, sizeof(buf), "%s?%s=%s&c=%s&g=%s", hgTrackUi, cartSessionVarName(), cartSessionId(cart), chromName, encodedMapName);
freeMem(encodedMapName);
return(cloneString(buf));
}
#ifdef REMOTE_TRACK_AJAX_CALLBACK
static boolean trackUsesRemoteData(struct track *track)
/* returns TRUE is this track has a remote datasource */
{
if (!IS_KNOWN(track->remoteDataSource))
{
SET_TO_NO(track->remoteDataSource);
//if (track->bbiFile != NULL) // FIXME: Chicken or the egg. bigWig/bigBed "bbiFile" filled
// // in by loadItems, but we don't want to load items.
// {
// if (!startsWith("/gbdb/",track->bbiFile->fileName))
// SET_TO_YES(track->remoteDataSource);
// }
if (startsWithWord("bigWig",track->tdb->type) || startsWithWord("bigBed",track->tdb->type) ||
startsWithWord("halSnake",track->tdb->type) ||
startsWithWord("pslSnake",track->tdb->type) ||
startsWithWord("bigPsl",track->tdb->type) ||
startsWithWord("bigGenePred",track->tdb->type) ||
startsWithWord("bigChain",track->tdb->type) ||
startsWithWord("bigMaf",track->tdb->type) ||
startsWithWord("bam",track->tdb->type) || startsWithWord("vcfTabix", track->tdb->type))
{
SET_TO_YES(track->remoteDataSource);
}
}
return IS_YES(track->remoteDataSource);
}
boolean trackShouldUseAjaxRetrieval(struct track *track)
/* Tracks with remote data sources should berendered via an ajax callback */
{
return (theImgBox && !trackImgOnly && trackUsesRemoteData(track));
}
#endif///def REMOTE_TRACK_AJAX_CALLBACK
static boolean isCompositeInAggregate(struct track *track)
// Check to see if this is a custom composite in aggregate mode.
{
if (!isCustomComposite(track->tdb))
return FALSE;
char *aggregateVal = cartOrTdbString(cart, track->tdb, "aggregate", NULL);
if ((aggregateVal == NULL) || sameString(aggregateVal, "none"))
return FALSE;
struct track *subtrack = NULL;
for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next)
{
if (isSubtrackVisible(subtrack))
break;
}
if (subtrack == NULL)
return FALSE;
for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next)
{
subtrack->mapsSelf = FALSE; /* Round about way to tell wig not to do own mapping. */
}
multiWigContainerMethods(track);
//struct wigCartOptions *wigCart = wigCartOptionsNew(cart, track->tdb, 0, NULL);
//track->wigCartData = (void *) wigCart;
//track->lineHeight = wigCart->defaultHeight;
//wigCart->isMultiWig = TRUE;
//wigCart->autoScale = wiggleScaleAuto;
//wigCart->defaultHeight = track->lineHeight;
//struct wigGraphOutput *wgo = setUpWgo(xOff, yOff, width, tg->height, numTracks, wigCart, hvg);
//tg->wigGraphOutput = wgo;
return TRUE;
}
static int trackPlusLabelHeight(struct track *track, int fontHeight)
/* Return the sum of heights of items in this track (or subtrack as it may be)
* and the center label(s) above the items (if any). */
{
if (trackShouldUseAjaxRetrieval(track))
return REMOTE_TRACK_HEIGHT;
enum trackVisibility vis = limitVisibility(track);
int y = track->totalHeight(track, vis);
if (isCenterLabelIncluded(track))
y += fontHeight;
if (tdbIsComposite(track->tdb) && !isCompositeInAggregate(track))
{
struct track *subtrack;
for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next)
{
if (isSubtrackVisible(subtrack) && isCenterLabelIncluded(subtrack))
y += fontHeight;
}
}
return y;
}
void drawColoredButtonBox(struct hvGfx *hvg, int x, int y, int w, int h,
int enabled, Color shades[])
/* draw button box, providing shades of the desired button color */
{
int light = shades[1], mid = shades[2], dark = shades[4];
if (enabled)
{
hvGfxBox(hvg, x, y, w, 1, light);
hvGfxBox(hvg, x, y+1, 1, h-1, light);
hvGfxBox(hvg, x+1, y+1, w-2, h-2, mid);
hvGfxBox(hvg, x+1, y+h-1, w-1, 1, dark);
hvGfxBox(hvg, x+w-1, y+1, 1, h-1, dark);
}
else /* try to make the button look as if
* it is already depressed */
{
hvGfxBox(hvg, x, y, w, 1, dark);
hvGfxBox(hvg, x, y+1, 1, h-1, dark);
hvGfxBox(hvg, x+1, y+1, w-2, h-2, light);
hvGfxBox(hvg, x+1, y+h-1, w-1, 1, light);
hvGfxBox(hvg, x+w-1, y+1, 1, h-1, light);
}
}
void drawGrayButtonBox(struct hvGfx *hvg, int x, int y, int w, int h, int enabled)
/* Draw a gray min-raised looking button. */
{
drawColoredButtonBox(hvg, x, y, w, h, enabled, shadesOfGray);
}
void drawBlueButtonBox(struct hvGfx *hvg, int x, int y, int w, int h, int enabled)
/* Draw a blue min-raised looking button. */
{
drawColoredButtonBox(hvg, x, y, w, h, enabled, shadesOfSea);
}
void drawButtonBox(struct hvGfx *hvg, int x, int y, int w, int h, int enabled)
/* Draw a standard (gray) min-raised looking button. */
{
drawGrayButtonBox(hvg, x, y, w, h, enabled);
}
void beforeFirstPeriod( char *str )
{
char *t = rindex( str, '.' );
if( t == NULL )
return;
else
str[strlen(str) - strlen(t)] = '\0';
}
static void drawBases(struct hvGfx *hvg, int x, int y, int width, int height,
Color color, MgFont *font, boolean complementSeq,
struct dnaSeq *thisSeq)
/* Draw evenly spaced bases. */
{
struct dnaSeq *seq;
if (thisSeq == NULL)
seq = hDnaFromSeq(database, chromName, winStart, winEnd, dnaUpper);
else
seq = thisSeq;
if (complementSeq)
complement(seq->dna, seq->size);
spreadBasesString(hvg, x, y, width, height, color, font,
seq->dna, seq->size, FALSE);
if (thisSeq == NULL)
freeDnaSeq(&seq);
}
void drawComplementArrow( struct hvGfx *hvg, int x, int y,
int width, int height, MgFont *font)
/* Draw arrow and create clickbox for complementing ruler bases */
{
boolean baseCmpl = cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE);
// reverse arrow when base complement doesn't match display
char *text = (baseCmpl == revCmplDisp) ? "--->" : "<---";
hvGfxTextRight(hvg, x, y, width, height, MG_BLACK, font, text);
mapBoxToggleComplement(hvg, x, y, width, height, NULL, chromName, winStart, winEnd,
"complement bases");
}
struct track *chromIdeoTrack(struct track *trackList)
/* Find chromosome ideogram track */
{
struct track *track;
for(track = trackList; track != NULL; track = track->next)
{
if(sameString(trackHubSkipHubName(track->track), "cytoBandIdeo"))
{
if (hTableExists(database, track->table))
return track;
else
return NULL;
}
}
return NULL;
}
void removeTrackFromGroup(struct track *track)
/* Remove track from group it is part of. */
{
struct trackRef *tr = NULL;
for(tr = track->group->trackList; tr != NULL; tr = tr->next)
{
if(tr->track == track)
{
slRemoveEl(&track->group->trackList, tr);
break;
}
}
}
void fillInStartEndBands(struct track *ideoTrack, char *startBand, char *endBand, int buffSize)
/* Loop through the bands and fill in the one that the current window starts
on and ends on. */
{
struct cytoBand *cb = NULL, *cbList = ideoTrack->items;
for(cb = cbList; cb != NULL; cb = cb->next)
{
/* If the start or end is encompassed by this band fill
it in. */
if (winStart >= cb->chromStart
&& winStart <= cb->chromEnd)
{
safef(startBand, buffSize, "%s", cb->name);
}
/* End is > rather than >= due to odditiy in the
cytoband track where the starts and ends of two
bands overlaps by one. */
if (winEnd > cb->chromStart
&& winEnd <= cb->chromEnd)
{
safef(endBand, buffSize, "%s", cb->name);
}
}
}
boolean makeChromIdeoImage(struct track **pTrackList, char *psOutput,
struct tempName *ideoTn)
/* Make an ideogram image of the chromosome and our position in it. If the
* ideoTn parameter is not NULL, it is filled in if the ideogram is created. */
{
struct track *ideoTrack = NULL;
MgFont *font = tl.font;
char *mapName = "ideoMap";
struct hvGfx *hvg;
boolean doIdeo = TRUE;
int ideoWidth = round(.8 *tl.picWidth);
int ideoHeight = 0;
int textWidth = 0;
struct tempName pngTn;
boolean nukeIdeoFromList = FALSE;
if (ideoTn == NULL)
ideoTn = &pngTn; // not returning value
ideoTrack = chromIdeoTrack(*pTrackList);
/* If no ideogram don't draw. */
if(ideoTrack == NULL)
{
doIdeo = FALSE;
}
else if(trackImgOnly && !ideogramToo)
{
doIdeo = FALSE;
}
else
{
/* Remove the track from the group and track list. */
removeTrackFromGroup(ideoTrack);
slRemoveEl(pTrackList, ideoTrack);
nukeIdeoFromList = TRUE;
/* Fix for hide all button hiding the ideogram as well. */
if(withIdeogram && ideoTrack->items == NULL)
{
ideoTrack->visibility = tvDense;
ideoTrack->loadItems(ideoTrack);
}
limitVisibility(ideoTrack);
/* If hidden don't draw. */
if(ideoTrack->limitedVis == tvHide || !withIdeogram)
doIdeo = FALSE;
}
// TODO use DIV in future (can update entire div at once in hgTracks.js)
//hPrintf("
\n");
// FYI from testing, I see that there is code that inserts warning error messages
// right before ideoMap, so any changes to that name or adding the DIV would require
// updating the warning-insertion target name.
if(doIdeo)
{
char startBand[1024];
char endBand[1024];
char title[1024];
startBand[0] = endBand[0] = '\0';
fillInStartEndBands(ideoTrack, startBand, endBand, sizeof(startBand));
/* Start up client side map. */
if (!psOutput)
hPrintf("\n");
jsInline("$('area.cytoBand').click(function(){return false;});\n");
}
}
// create an empty hidden-map place holder which can change dynamically with ajax callbacks.
if (!doIdeo && !psOutput)
{
hPrintf("\n");
}
hPrintf("
");
if (!psOutput)
{
// by default, create an empty hidden ideo place holder for future dynamic ajax update
char *srcPath = "";
char *style = "display: none;";
if (doIdeo)
{
srcPath = ideoTn->forHtml;
style = "display: inline;";
}
hPrintf("
\n"); // TODO use DIV in future
if(ideoTrack != NULL)
{
ideoTrack->limitedVisSet = TRUE;
ideoTrack->limitedVis = tvHide; /* Don't draw in main gif. */
}
return nukeIdeoFromList;
}
char *pcrResultMapItemName(struct track *tg, void *item)
/* Stitch accession and display name back together (if necessary). */
{
struct linkedFeatures *lf = item;
return pcrResultItemAccName(lf->name, lf->extra);
}
void pcrResultLoad(struct track *tg)
/* Load locations of primer matches into linkedFeatures items. */
{
char *pslFileName, *primerFileName;
struct targetDb *target;
if (! pcrResultParseCart(database, cart, &pslFileName, &primerFileName, &target))
return;
/* Don't free psl -- used in drawing phase by baseColor code. */
struct psl *pslList = pslLoadAll(pslFileName), *psl;
struct linkedFeatures *itemList = NULL;
if (target != NULL)
{
int rowOffset = hOffsetPastBin(database, chromName, target->pslTable);
struct sqlConnection *conn = hAllocConn(database);
struct sqlResult *sr;
char **row;
char query[2048];
struct psl *tpsl;
for (tpsl = pslList; tpsl != NULL; tpsl = tpsl->next)
{
char *itemAcc = pcrResultItemAccession(tpsl->tName);
char *itemName = pcrResultItemName(tpsl->tName);
/* Query target->pslTable to get target-to-genomic mapping: */
sqlSafef(query, sizeof(query), "select * from %s where qName = '%s'",
target->pslTable, itemAcc);
sr = sqlGetResult(conn, query);
while ((row = sqlNextRow(sr)) != NULL)
{
struct psl *gpsl = pslLoad(row+rowOffset);
if (sameString(gpsl->tName, chromName) && gpsl->tStart < winEnd && gpsl->tEnd > winStart)
{
struct psl *trimmed = pslTrimToQueryRange(gpsl, tpsl->tStart,
tpsl->tEnd);
struct linkedFeatures *lf;
char *targetStyle = cartUsualString(cart,
PCR_RESULT_TARGET_STYLE, PCR_RESULT_TARGET_STYLE_DEFAULT);
if (sameString(targetStyle, PCR_RESULT_TARGET_STYLE_TALL))
{
lf = lfFromPslx(gpsl, 1, FALSE, FALSE, tg);
lf->tallStart = trimmed->tStart;
lf->tallEnd = trimmed->tEnd;
}
else
{
lf = lfFromPslx(trimmed, 1, FALSE, FALSE, tg);
}
lf->name = cloneString(itemAcc);
char extraInfo[512];
safef(extraInfo, sizeof(extraInfo), "%s|%d|%d",
(itemName ? itemName : ""), tpsl->tStart, tpsl->tEnd);
lf->extra = cloneString(extraInfo);
slAddHead(&itemList, lf);
}
}
}
hFreeConn(&conn);
}
else
for (psl = pslList; psl != NULL; psl = psl->next)
if (sameString(psl->tName, chromName) && psl->tStart < winEnd && psl->tEnd > winStart)
{
struct linkedFeatures *lf = lfFromPslx(psl, 1, FALSE, FALSE, tg);
lf->name = cloneString("");
lf->extra = cloneString("");
slAddHead(&itemList, lf);
}
slSort(&itemList, linkedFeaturesCmp);
tg->items = itemList;
}
char *pcrResultTrackItemName(struct track *tg, void *item)
/* If lf->extra is non-empty, return it (display name for item).
* Otherwise default to item name. */
{
struct linkedFeatures *lf = item;
char *extra = (char *)lf->extra;
if (isNotEmpty(extra))
{
static char displayName[512];
safecpy(displayName, sizeof(displayName), extra);
char *ptr = strchr(displayName, '|');
if (ptr != NULL)
*ptr = '\0';
if (isNotEmpty(displayName))
return displayName;
}
return lf->name;
}
struct track *pcrResultTg()
/* Make track of hgPcr results (alignments of user's submitted primers). */
{
struct trackDb *tdb = pcrResultFakeTdb();
struct track *tg = trackFromTrackDb(tdb);
tg->loadItems = pcrResultLoad;
tg->itemName = pcrResultTrackItemName;
tg->mapItemName = pcrResultMapItemName;
tg->exonArrows = TRUE;
tg->hasUi = TRUE;
return tg;
}
struct track *linkedFeaturesTg()
/* Return generic track for linked features. */
{
struct track *tg = trackNew();
linkedFeaturesMethods(tg);
tg->colorShades = shadesOfGray;
return tg;
}
void setTgDarkLightColors(struct track *tg, int r, int g, int b)
/* Set track color to r,g,b. Set altColor to a lighter version
* of the same. */
{
tg->colorShades = NULL;
tg->color.r = r;
tg->color.g = g;
tg->color.b = b;
tg->altColor.r = (r+255)/2;
tg->altColor.g = (g+255)/2;
tg->altColor.b = (b+255)/2;
}
void parseSs(char *ss, char **retPsl, char **retFa)
/* Parse out ss variable into components. */
{
static char buf[1024];
char *words[2];
int wordCount;
safecpy(buf, sizeof(buf), ss);
wordCount = chopLine(buf, words);
if (wordCount < 2)
errAbort("Badly formated ss variable");
*retPsl = words[0];
*retFa = words[1];
}
boolean ssFilesExist(char *ss)
/* Return TRUE if both files in ss exist. */
{
char *faFileName, *pslFileName;
parseSs(ss, &pslFileName, &faFileName);
return fileExists(pslFileName) && fileExists(faFileName);
}
void loadUserPsl(struct track *tg)
/* Load up hgBlat results from table into track items. */
{
char *ss = userSeqString;
char buf2[3*512];
char *faFileName, *pslFileName;
struct lineFile *f;
struct psl *psl;
struct linkedFeatures *lfList = NULL, *lf;
enum gfType qt, tt;
int sizeMul = 1;
parseSs(ss, &pslFileName, &faFileName);
pslxFileOpen(pslFileName, &qt, &tt, &f);
if (qt == gftProt)
{
setTgDarkLightColors(tg, 0, 80, 150);
tg->colorShades = NULL;
sizeMul = 3;
}
tg->itemName = linkedFeaturesName;
while ((psl = pslNext(f)) != NULL)
{
if (sameString(psl->tName, chromName) && psl->tStart < winEnd && psl->tEnd > winStart)
{
lf = lfFromPslx(psl, sizeMul, TRUE, FALSE, tg);
sprintf(buf2, "%s %s", ss, psl->qName);
lf->extra = cloneString(buf2);
slAddHead(&lfList, lf);
/* Don't free psl -- used in drawing phase by baseColor code. */
}
else
pslFree(&psl);
}
slSort(&lfList, linkedFeaturesCmpStart);
lineFileClose(&f);
tg->items = lfList;
}
static void addUserSeqBaseAndIndelSettings(struct trackDb *tdb)
/* If user sequence is a dna or rna alignment, add settings to enable
* base-level differences and indel display. */
{
enum gfType qt, tt;
struct lineFile *lf;
char *faFileName, *pslFileName;
parseSs(userSeqString, &pslFileName, &faFileName);
pslxFileOpen(pslFileName, &qt, &tt, &lf);
lineFileClose(&lf);
if (qt != gftProt)
{
if (tdb->settingsHash == NULL)
tdb->settingsHash = hashNew(0);
hashAdd(tdb->settingsHash, BASE_COLOR_DEFAULT, cloneString("diffBases"));
hashAdd(tdb->settingsHash, BASE_COLOR_USE_SEQUENCE, cloneString("ss"));
hashAdd(tdb->settingsHash, SHOW_DIFF_BASES_ALL_SCALES, cloneString("."));
hashAdd(tdb->settingsHash, INDEL_DOUBLE_INSERT, cloneString("on"));
hashAdd(tdb->settingsHash, INDEL_QUERY_INSERT, cloneString("on"));
hashAdd(tdb->settingsHash, INDEL_POLY_A, cloneString("on"));
}
}
struct track *userPslTg()
/* Make track of user pasted sequence. */
{
struct track *tg = linkedFeaturesTg();
struct trackDb *tdb;
tg->track = "hgUserPsl";
tg->table = tg->track;
tg->canPack = TRUE;
tg->visibility = tvPack;
tg->longLabel = "Your Sequence from Blat Search";
tg->shortLabel = "Blat Sequence";
tg->loadItems = loadUserPsl;
tg->mapItemName = lfMapNameFromExtra;
tg->priority = 103;
tg->defaultPriority = tg->priority;
tg->groupName = "map";
tg->defaultGroupName = cloneString(tg->groupName);
tg->exonArrows = TRUE;
/* better to create the tdb first, then use trackFromTrackDb */
AllocVar(tdb);
tdb->track = cloneString(tg->track);
tdb->table = cloneString(tg->table);
tdb->visibility = tg->visibility;
tdb->shortLabel = cloneString(tg->shortLabel);
tdb->longLabel = cloneString(tg->longLabel);
tdb->grp = cloneString(tg->groupName);
tdb->priority = tg->priority;
tdb->type = cloneString("psl");
tdb->canPack = tg->canPack;
trackDbPolish(tdb);
addUserSeqBaseAndIndelSettings(tdb);
tg->tdb = tdb;
return tg;
}
char *oligoMatchSeq()
/* Return sequence for oligo matching. */
{
char *s = cartOptionalString(cart, oligoMatchVar);
if (s != NULL)
{
int len;
tolowers(s);
iupacFilter(s, s);
len = strlen(s);
if (len < 2)
s = NULL;
}
if (s == NULL)
s = cloneString(oligoMatchDefault);
return s;
}
char *oligoMatchName(struct track *tg, void *item)
/* Return name for oligo, which is just the base position. */
{
struct bed *bed = item;
static char buf[22];
buf[0] = bed->strand[0];
sprintLongWithCommas(buf+1, bed->chromStart+1);
return buf;
}
char *dnaInWindow()
/* This returns the DNA in the window, all in lower case. */
{
static struct dnaSeq *seq = NULL;
if (seq == NULL)
seq = hDnaFromSeq(database, chromName, winStart, winEnd, dnaLower);
return seq->dna;
}
char *stringInWrapper(char *needle, char *haystack)
/* Wrapper around string in to make it so it's a function rather than a macro. */
{
return stringIn(needle, haystack);
}
void oligoMatchLoad(struct track *tg)
/* Create track of perfect matches to oligo on either strand.
*
* Note that if you are extending this code, there is also a parallel copy
* in src/hg/utils/oligoMatch/ that should be kept up to date!
*
*/
{
char *dna = dnaInWindow();
char *fOligo = oligoMatchSeq();
char *(*finder)(char *needle, char *haystack) = (anyIupac(fOligo) ? iupacIn : stringInWrapper);
int oligoSize = strlen(fOligo);
char *rOligo = cloneString(fOligo);
char *rMatch = NULL, *fMatch = NULL;
struct bed *bedList = NULL, *bed;
char strand;
int count = 0, maxCount = 1000000;
if (oligoSize >= 2)
{
fMatch = finder(fOligo, dna);
iupacReverseComplement(rOligo, oligoSize);
if (sameString(rOligo, fOligo))
rOligo = NULL;
else
rMatch = finder(rOligo, dna);
for (;;)
{
char *oneMatch = NULL;
if (rMatch == NULL)
{
if (fMatch == NULL)
break;
else
{
oneMatch = fMatch;
fMatch = finder(fOligo, fMatch+1);
strand = '+';
}
}
else if (fMatch == NULL)
{
oneMatch = rMatch;
rMatch = finder(rOligo, rMatch+1);
strand = '-';
}
else if (rMatch < fMatch)
{
oneMatch = rMatch;
rMatch = finder(rOligo, rMatch+1);
strand = '-';
}
else
{
oneMatch = fMatch;
fMatch = finder(fOligo, fMatch+1);
strand = '+';
}
if (count < maxCount)
{
++count;
AllocVar(bed);
bed->chromStart = winStart + (oneMatch - dna);
bed->chromEnd = bed->chromStart + oligoSize;
bed->strand[0] = strand;
slAddHead(&bedList, bed);
}
else
break;
}
slReverse(&bedList);
if (count < maxCount)
tg->items = bedList;
else
warn("More than %d items in %s, suppressing display", maxCount, tg->shortLabel);
}
}
struct track *oligoMatchTg()
/* Make track of perfect matches to oligomer. */
{
struct track *tg = trackNew();
char *oligo = oligoMatchSeq();
int oligoSize = strlen(oligo);
char *medOligo = cloneString(oligo);
static char longLabel[80];
struct trackDb *tdb;
/* Generate abbreviated strings. */
if (oligoSize >= 30)
{
memset(medOligo + 30-3, '.', 3);
medOligo[30] = 0;
}
touppers(medOligo);
bedMethods(tg);
tg->track = "oligoMatch";
tg->table = tg->track;
tg->canPack = TRUE;
tg->visibility = tvHide;
tg->hasUi = TRUE;
tg->shortLabel = cloneString(OLIGO_MATCH_TRACK_LABEL);
safef(longLabel, sizeof(longLabel),
"Perfect Matches to Short Sequence (%s)", medOligo);
tg->longLabel = longLabel;
tg->loadItems = oligoMatchLoad;
tg->itemName = oligoMatchName;
tg->mapItemName = oligoMatchName;
tg->priority = OLIGO_MATCH_TRACK_PRIORITY;
tg->defaultPriority = tg->priority;
tg->groupName = "map";
tg->defaultGroupName = cloneString(tg->groupName);
AllocVar(tdb);
tdb->track = cloneString(tg->track);
tdb->table = cloneString(tg->table);
tdb->visibility = tg->visibility;
tdb->shortLabel = cloneString(tg->shortLabel);
tdb->longLabel = cloneString(tg->longLabel);
tdb->grp = cloneString(tg->groupName);
tdb->priority = tg->priority;
tdb->canPack = tg->canPack;
trackDbPolish(tdb);
tg->tdb = tdb;
return tg;
}
Color maybeDarkerLabels(struct track *track, struct hvGfx *hvg, Color color)
/* For tracks having light track display but needing a darker label */
{
if (trackDbSetting(track->tdb, "darkerLabels"))
{
struct hsvColor hsv = mgRgbToHsv(mgColorIxToRgb(NULL, color));
// check if really pale
if (hsv.s < 500 ||(hsv.h > 40.0 && hsv.h < 150.0))
return somewhatDarkerColor(hvg, color);
return slightlyDarkerColor(hvg, color);
}
return color;
}
boolean isCenterLabelsPackOff(struct track *track)
/* Check for trackDb setting to suppress center labels of composite in pack mode */
{
if (!track || !track->tdb)
return FALSE;
char *centerLabelsPack = trackDbSetting(track->tdb, "centerLabelsPack");
return (centerLabelsPack && sameWord(centerLabelsPack, "off"));
}
static int doLeftLabels(struct track *track, struct hvGfx *hvg, MgFont *font,
int y)
/* Draw left labels. Return y coord. */
{
struct slList *prev = NULL;
/* for sample tracks */
double minRangeCutoff, maxRangeCutoff;
double minRange, maxRange;
double min0, max0;
char minRangeStr[32];
char maxRangeStr[32];
int ymin, ymax;
int newy;
char o4[256];
char o5[256];
struct slList *item;
enum trackVisibility vis = track->limitedVis;
Color labelColor = (track->labelColor ?
track->labelColor : track->ixColor);
labelColor = maybeDarkerLabels(track, hvg, labelColor);
int fontHeight = mgFontLineHeight(font);
int tHeight = trackPlusLabelHeight(track, fontHeight);
if (vis == tvHide)
return y;
/* if a track can do its own left labels, do them after drawItems */
if (track->drawLeftLabels != NULL)
return y + tHeight;
/* Wiggle tracks depend upon clipping. They are reporting
* totalHeight artifically high by 1 so this will leave a
* blank area one pixel high below the track.
*/
if (sameString("wig",track->tdb->type) || sameString("bedGraph",track->tdb->type))
hvGfxSetClip(hvg, leftLabelX, y, leftLabelWidth, tHeight-1);
else
hvGfxSetClip(hvg, leftLabelX, y, leftLabelWidth, tHeight);
minRange = 0.0;
safef( o4, sizeof(o4),"%s.min.cutoff", track->track);
safef( o5, sizeof(o5),"%s.max.cutoff", track->track);
minRangeCutoff = max( atof(cartUsualString(cart,o4,"0.0"))-0.1,
track->minRange );
maxRangeCutoff = min( atof(cartUsualString(cart,o5,"1000.0"))+0.1,
track->maxRange);
if( sameString( track->table, "humMusL" ) ||
sameString( track->table, "musHumL" ) ||
sameString( track->table, "mm3Rn2L" ) ||
sameString( track->table, "hg15Mm3L" ) ||
sameString( track->table, "mm3Hg15L" ) ||
sameString( track->table, "regpotent" ) ||
sameString( track->table, "HMRConservation" ) )
{
int binCount = round(1.0/track->scaleRange);
minRange = whichSampleBin( minRangeCutoff, track->minRange, track->maxRange, binCount );
maxRange = whichSampleBin( maxRangeCutoff, track->minRange, track->maxRange, binCount );
min0 = whichSampleNum( minRange, track->minRange,track->maxRange, binCount );
max0 = whichSampleNum( maxRange, track->minRange, track->maxRange, binCount );
sprintf( minRangeStr, " " );
sprintf( maxRangeStr, " " );
if( vis == tvFull && track->heightPer >= 74 )
{
samplePrintYAxisLabel( hvg, y+5, track, "1.0", min0, max0 );
samplePrintYAxisLabel( hvg, y+5, track, "2.0", min0, max0 );
samplePrintYAxisLabel( hvg, y+5, track, "3.0", min0, max0 );
samplePrintYAxisLabel( hvg, y+5, track, "4.0", min0, max0 );
samplePrintYAxisLabel( hvg, y+5, track, "5.0", min0, max0 );
samplePrintYAxisLabel( hvg, y+5, track, "6.0", min0, max0 );
}
}
else
{
sprintf( minRangeStr, "%d", (int)round(minRangeCutoff));
sprintf( maxRangeStr, "%d", (int)round(maxRangeCutoff));
}
/* special label handling for wigMaf type tracks -- they
display a left label in pack mode. To use the full mode
labeling, temporarily set visibility to full.
Restore savedVis later */
if (startsWith("bigMaf", track->tdb->type) || startsWith("wigMaf", track->tdb->type) || startsWith("maf", track->tdb->type))
vis = tvFull;
/* behave temporarily like pack for these */
if (track->limitedVis == tvFull && isTypeBedLike(track))
vis = tvPack;
switch (vis)
{
case tvHide:
break; /* Do nothing; */
case tvPack:
if (isCenterLabelsPackOff(track))
// draw left labels for pack mode track with center labels off
{
if (isCenterLabelIncluded(track))
y += fontHeight;
hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth-1, track->lineHeight, labelColor, font,
track->shortLabel);
y += track->height;
}
else
y += tHeight;
break;
case tvSquish:
y += tHeight;
break;
case tvFull:
if (isCenterLabelIncluded(track))
y += fontHeight;
if( track->subType == lfSubSample && track->items == NULL )
y += track->height;
for (item = track->items; item != NULL; item = item->next)
{
char *rootName;
char *name = track->itemName(track, item);
int itemHeight = track->itemHeight(track, item);
//warn(" track %s, itemHeight %d\n", track->shortLabel, itemHeight);
newy = y;
if (track->itemLabelColor != NULL)
labelColor = track->itemLabelColor(track, item, hvg);
else
labelColor = maybeDarkerLabels(track, hvg, labelColor);
/* Do some fancy stuff for sample tracks.
* Draw y-value limits for 'sample' tracks. */
if (track->subType == lfSubSample )
{
if( prev == NULL )
newy += itemHeight;
else
newy += sampleUpdateY(name,
track->itemName(track, prev), itemHeight);
if( newy == y )
continue;
if( track->heightPer > (3 * fontHeight ) )
{
ymax = y - (track->heightPer / 2) + (fontHeight / 2);
ymin = y + (track->heightPer / 2) - (fontHeight / 2);
hvGfxTextRight(hvg, leftLabelX, ymin, leftLabelWidth-1,
itemHeight, track->ixAltColor,
font, minRangeStr );
hvGfxTextRight(hvg, leftLabelX, ymax, leftLabelWidth-1,
itemHeight, track->ixAltColor,
font, maxRangeStr );
}
prev = item;
rootName = cloneString( name );
beforeFirstPeriod( rootName );
if( sameString( track->table, "humMusL" ) ||
sameString( track->table, "hg15Mm3L" ))
hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth - 1,
itemHeight, track->ixColor, font, "Mouse Cons");
else if( sameString( track->table, "musHumL" ) ||
sameString( track->table, "mm3Hg15L"))
hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth - 1,
itemHeight, track->ixColor, font, "Human Cons");
else if( sameString( track->table, "mm3Rn2L" ))
hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth - 1,
itemHeight, track->ixColor, font, "Rat Cons");
else
hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth - 1,
itemHeight, track->ixColor, font, rootName );
freeMem( rootName );
y = newy;
}
else
{
/* standard item labeling */
if (highlightItem(track, item))
{
int nameWidth = mgFontStringWidth(font, name);
int boxStart = leftLabelX + leftLabelWidth - 2 - nameWidth;
hvGfxBox(hvg, boxStart, y, nameWidth+1, itemHeight - 1,
labelColor);
hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth-1,
itemHeight, MG_WHITE, font, name);
}
else
hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth - 1,
itemHeight, labelColor, font, name);
y += itemHeight;
}
}
break;
case tvDense:
if (isCenterLabelIncluded(track))
y += fontHeight;
/*draw y-value limits for 'sample' tracks.
* (always puts 0-100% range)*/
if (track->subType == lfSubSample && track->heightPer > (3 * fontHeight))
{
int ymax = y - (track->heightPer / 2) + (fontHeight / 2);
int ymin = y + (track->heightPer / 2) - (fontHeight / 2);
hvGfxTextRight(hvg, leftLabelX, ymin,
leftLabelWidth-1, track->lineHeight,
track->ixAltColor, font, minRangeStr );
hvGfxTextRight(hvg, leftLabelX, ymax,
leftLabelWidth-1, track->lineHeight,
track->ixAltColor, font, maxRangeStr );
}
hvGfxTextRight(hvg, leftLabelX, y, leftLabelWidth-1,
track->lineHeight, labelColor, font,
track->shortLabel);
y += track->height;
break;
}
hvGfxUnclip(hvg);
return y;
}
void setGlobalsFromWindow(struct window *window); // FORWARD DECLARATION
static void doLabelNextItemButtons(struct track *track, struct track *parentTrack,
struct hvGfx *hvg, MgFont *font, int y,
int trackPastTabX, int trackPastTabWidth, int fontHeight,
int insideHeight, Color labelColor)
/* If the track allows label next-item buttons (next gene), draw them. */
/* The button will cause hgTracks to run again with the additional CGI */
/* vars nextItem=trackName or prevItem=trackName, which will then */
/* signal the browser to find the next thing on the track before it */
/* does anything else. */
{
int portWidth = fullInsideWidth;
int portX = fullInsideX;
// If a portal was established, then set the portal dimensions
long portalStart,chromStart;
double basesPerPixel;
if (theImgBox
&& imgBoxPortalDimensions(theImgBox,&chromStart,NULL,NULL,NULL,&portalStart,NULL,
&portWidth,&basesPerPixel))
{
portX = (int)((portalStart - chromStart) / basesPerPixel);
portX += gfxBorder;
if (withLeftLabels)
portX += tl.leftLabelWidth + gfxBorder;
portWidth = portWidth-gfxBorder-insideX;
}
int arrowWidth = insideHeight;
int arrowButtonWidth = arrowWidth + 2 * NEXT_ITEM_ARROW_BUFFER;
int rightButtonX = portX + portWidth - arrowButtonWidth - 1;
char buttonText[256];
Color fillColor = lightGrayIndex();
labelColor = blackIndex();
hvGfxNextItemButton(hvg, rightButtonX + NEXT_ITEM_ARROW_BUFFER, y, arrowWidth, arrowWidth,
labelColor, fillColor, TRUE);
hvGfxNextItemButton(hvg, portX + NEXT_ITEM_ARROW_BUFFER, y, arrowWidth, arrowWidth,
labelColor, fillColor, FALSE);
safef(buttonText, ArraySize(buttonText), "hgt.prevItem=%s", track->track);
mapBoxReinvoke(hvg, portX, y + 1, arrowButtonWidth, insideHeight, track, FALSE,
NULL, 0, 0, (revCmplDisp ? "Next item" : "Prev item"), buttonText);
#ifdef IMAGEv2_SHORT_TOGGLE
char *label = (theImgBox ? track->longLabel : parentTrack->longLabel);
int width = portWidth - (2 * arrowButtonWidth);
int x = portX + arrowButtonWidth;
// make toggle cover only actual label
int size = mgFontStringWidth(font,label) + 12; // get close enough to the label
if (width > size)
{
x += width/2 - size/2;
width = size;
}
mapBoxToggleVis(hvg, x, y + 1, width, insideHeight, (theImgBox ? track : parentTrack));
#else///ifndef IMAGEv2_SHORT_TOGGLE
mapBoxToggleVis(hvg, portX + arrowButtonWidth, y + 1, portWidth - (2 * arrowButtonWidth),
insideHeight, (theImgBox ? track : parentTrack));
#endif///ndef IMAGEv2_SHORT_TOGGLE
// use the last window globals instead of the first
struct window *w=windows;
while(w->next)
w = w->next;
setGlobalsFromWindow(w); // use last window
safef(buttonText, ArraySize(buttonText), "hgt.nextItem=%s", track->track);
mapBoxReinvoke(hvg, portX + portWidth - arrowButtonWidth, y + 1, arrowButtonWidth, insideHeight,
track, FALSE, NULL, 0, 0, (revCmplDisp ? "Prev item" : "Next item"), buttonText);
setGlobalsFromWindow(windows); // restore first window
}
static int doCenterLabels(struct track *track, struct track *parentTrack,
struct hvGfx *hvg, MgFont *font, int y, int fullInsideWidth )
/* Draw center labels. Return y coord */
{
if (track->limitedVis != tvHide)
{
if (isCenterLabelIncluded(track))
{
int trackPastTabX = (withLeftLabels ? trackTabWidth : 0);
int trackPastTabWidth = tl.picWidth - trackPastTabX;
int fontHeight = mgFontLineHeight(font);
int insideHeight = fontHeight-1;
boolean toggleDone = FALSE;
char *label = track->longLabel;
Color labelColor = (track->labelColor ?
track->labelColor : track->ixColor);
if (isCenterLabelConditional(track))
{
struct trackDb* tdbComposite = tdbGetComposite(track->tdb);
if (tdbComposite != NULL)
{
label = tdbComposite->longLabel;
labelColor = hvGfxFindColorIx(hvg, tdbComposite->colorR,
tdbComposite->colorG, tdbComposite->colorB);
}
}
labelColor = maybeDarkerLabels(track, hvg, labelColor);
hvGfxTextCentered(hvg, insideX, y+1, fullInsideWidth, insideHeight,
labelColor, font, label);
if (track->nextItemButtonable && track->nextPrevItem && !tdbIsComposite(track->tdb))
{
if (withNextItemArrows || trackDbSettingOn(track->tdb, "nextItemButton"))
{
doLabelNextItemButtons(track, parentTrack, hvg, font, y, trackPastTabX,
trackPastTabWidth, fontHeight, insideHeight, labelColor);
toggleDone = TRUE;
}
}
if (!toggleDone)
{
#ifdef IMAGEv2_SHORT_TOGGLE
// make toggle cover only actual label
int size = mgFontStringWidth(font,label) + 12; // get close enough to the label
if (trackPastTabWidth > size)
{
trackPastTabX = insideX + insideWidth/2 - size/2;
trackPastTabWidth = size;
}
#endif///def IMAGEv2_SHORT_TOGGLE
mapBoxToggleVis(hvg, trackPastTabX, y+1,trackPastTabWidth, insideHeight,
(theImgBox ? track : parentTrack));
}
y += fontHeight;
}
y += track->totalHeight(track, track->limitedVis);
}
return y;
}
static void doPreDrawItems(struct track *track, struct hvGfx *hvg, MgFont *font,
int y, long *lastTime)
/* Do Pre-Draw track items. */
{
int fontHeight = mgFontLineHeight(font);
if (isCenterLabelIncluded(track))
y += fontHeight;
if (track->preDrawItems)
track->preDrawItems(track, winStart, winEnd, hvg, insideX, y, insideWidth,
font, track->ixColor, track->limitedVis);
if (measureTiming && lastTime)
{
long thisTime = clock1000();
track->drawTime = thisTime - *lastTime;
*lastTime = thisTime;
}
}
static int doDrawItems(struct track *track, struct hvGfx *hvg, MgFont *font,
int y, long *lastTime)
/* Draw track items. Return y coord */
{
int fontHeight = mgFontLineHeight(font);
int pixWidth = tl.picWidth;
if (isCenterLabelIncluded(track))
y += fontHeight;
if (track->limitedVis == tvPack)
{
hvGfxSetClip(hvg, gfxBorder+trackTabWidth+1, y,
pixWidth-2*gfxBorder-trackTabWidth-1, track->height);
}
else
hvGfxSetClip(hvg, insideX, y, insideWidth, track->height);
track->drawItems(track, winStart, winEnd, hvg, insideX, y, insideWidth,
font, track->ixColor, track->limitedVis);
if (measureTiming && lastTime)
{
long thisTime = clock1000();
track->drawTime = thisTime - *lastTime;
*lastTime = thisTime;
}
hvGfxUnclip(hvg);
y += track->totalHeight(track, track->limitedVis);
return y;
}
static int doMapItems(struct track *track, struct hvGfx *hvg, int fontHeight, int y)
/* Draw map boxes around track items */
{
char *type = track->tdb->type;
int newy;
int trackPastTabX = (withLeftLabels ? trackTabWidth : 0);
int trackPastTabWidth = tl.picWidth - trackPastTabX;
int start = 1;
struct slList *item;
boolean isWig = (sameString("wig", type) || startsWith("wig ", type) ||
startsWith("bedGraph", type));
if (isCenterLabelIncluded(track))
y += fontHeight;
if (track->mapsSelf)
{
return y+track->height;
}
if (track->subType == lfSubSample && track->items == NULL)
y += track->lineHeight;
/* override doMapItems for hapmapLd track */
/* does not scale with subtracks right now, so this is commented out until it can be fixed
if (startsWith("hapmapLd",track->table))
{
y += round((double)(scaleForPixels(insideWidth)*insideWidth/2));
return y;
}
*/
for (item = track->items; item != NULL; item = item->next)
{
int height = track->itemHeight(track, item);
/*wiggle tracks don't always increment height (y-value) here*/
if( track->subType == lfSubSample )
{
newy = y;
if( !start && item->next != NULL )
{
newy += sampleUpdateY( track->itemName(track, item),
track->itemName(track, item->next),
height );
}
else if( item->next != NULL || start )
newy += height;
start = 0;
y = newy;
}
else
{
if (track->mapItem == NULL)
track->mapItem = genericMapItem;
if (!track->mapsSelf)
{
track->mapItem(track, hvg, item, track->itemName(track, item),
track->mapItemName(track, item),
track->itemStart(track, item),
track->itemEnd(track, item), trackPastTabX,
y, trackPastTabWidth,height);
}
y += height;
}
}
/* Wiggle track's ->height is actually one less than what it returns from
* totalHeight()... I think the least disruptive way to account for this
* (and not touch Ryan Weber's Sample stuff) is to just correct here if
* we see wiggle or bedGraph: */
if (isWig)
y++;
return y;
}
static int doOwnLeftLabels(struct track *track, struct hvGfx *hvg,
MgFont *font, int y)
/* Track draws it own, custom left labels */
{
int fontHeight = mgFontLineHeight(font);
int tHeight = trackPlusLabelHeight(track, fontHeight);
Color labelColor = (track->labelColor ? track->labelColor : track->ixColor);
labelColor = maybeDarkerLabels(track, hvg, labelColor);
hvGfxSetClip(hvg, leftLabelX, y, leftLabelWidth, tHeight);
track->drawLeftLabels(track, winStart, winEnd,
hvg, leftLabelX, y, leftLabelWidth, tHeight,
isCenterLabelIncluded(track), font, labelColor,
track->limitedVis);
hvGfxUnclip(hvg);
y += tHeight;
return y;
}
// defined below:
static int getMaxWindowToDraw(struct trackDb *tdb);
int doTrackMap(struct track *track, struct hvGfx *hvg, int y, int fontHeight,
int trackPastTabX, int trackPastTabWidth)
/* Write out the map for this track. Return the new offset. */
{
int mapHeight = 0;
switch (track->limitedVis)
{
case tvPack:
case tvSquish:
y += trackPlusLabelHeight(track, fontHeight);
break;
case tvFull:
if (!nextItemCompatible(track))
{
if (trackIsCompositeWithSubtracks(track)) // TODO: Change when tracks->subtracks
{ // are always set for composite
if (isCenterLabelIncluded(track))
y += fontHeight;
struct track *subtrack;
for (subtrack = track->subtracks; subtrack != NULL;subtrack = subtrack->next)
{
if (isSubtrackVisible(subtrack))
{
if (subtrack->limitedVis == tvFull)
y = doMapItems(subtrack, hvg, fontHeight, y);
else
{
if (isCenterLabelIncluded(subtrack))
y += fontHeight;
if (theImgBox && subtrack->limitedVis == tvDense)
mapBoxToggleVis(hvg, trackPastTabX, y, trackPastTabWidth,
track->lineHeight, subtrack);
y += subtrack->totalHeight(subtrack, subtrack->limitedVis);
}
}
}
}
else
y = doMapItems(track, hvg, fontHeight, y);
}
else
y += trackPlusLabelHeight(track, fontHeight);
break;
case tvDense:
if (isCenterLabelIncluded(track))
y += fontHeight;
if (tdbIsComposite(track->tdb))
mapHeight = track->height;
else
mapHeight = track->lineHeight;
int maxWinToDraw = getMaxWindowToDraw(track->tdb);
if (maxWinToDraw <= 1 || (winEnd - winStart) <= maxWinToDraw)
mapBoxToggleVis(hvg, trackPastTabX, y, trackPastTabWidth, mapHeight, track);
y += mapHeight;
break;
case tvHide:
default:
break; /* Do nothing; */
}
return y;
}
long computeScaleBar(long numBases, char scaleText[], int scaleTextSize)
/* Do some scalebar calculations and return the number of bases the scalebar will span. */
{
char *baseWord = "bases";
long scaleBases = 0;
int scaleBasesTextNum = 0;
int numFigs = (int)log10(numBases);
int frontNum = (int)(numBases/pow(10,numFigs));
if (frontNum == 1)
{
numFigs--;
scaleBases = 5 * pow(10, numFigs);
scaleBasesTextNum = 5 * pow(10, numFigs % 3);
}
else if ((frontNum > 1) && (frontNum <= 4))
{
scaleBases = pow(10, numFigs);
scaleBasesTextNum = pow(10, numFigs % 3);
}
else if (frontNum > 4)
{
scaleBases = 2 * pow(10, numFigs);
scaleBasesTextNum = 2 * pow(10, numFigs % 3);
}
if ((numFigs >= 3) && (numFigs < 6))
baseWord = "kb";
else if ((numFigs >= 6) && (numFigs < 9))
baseWord = "Mb";
else if ((numFigs >= 9) && (numFigs < 12))
baseWord = "Gb";
safef(scaleText, scaleTextSize, "%d %s", scaleBasesTextNum, baseWord);
return scaleBases;
}
enum trackVisibility limitedVisFromComposite(struct track *subtrack)
/* returns the subtrack visibility which may be limited by composite with multi-view dropdowns. */
{
if (tdbIsCompositeChild(subtrack->tdb))
{
if (!subtrack->limitedVisSet)
{
subtrack->visibility = tdbVisLimitedByAncestors(cart, subtrack->tdb, TRUE, TRUE);
limitVisibility(subtrack);
}
}
else
limitVisibility(subtrack);
return subtrack->limitedVis;
}
static int calcNewWinWidth(struct cart *cart, int winStart, int winEnd, int insideWidth)
/* Calc width of hit boxes that will zoom program around ruler. */
// TODO GALT
// probably should change this to use full and virt vars and make it
// go across the entire full image width. Especially since this is about
// zooming on the virt chrom when user clicks on scalebar or chrom-ruler
// Default is to just zoom current window in by 3x to 1/3 of current width.
{
int winWidth = winEnd - winStart;
int newWinWidth = winWidth;
char message[32];
char *zoomType = cartCgiUsualString(cart, RULER_BASE_ZOOM_VAR, ZOOM_3X);
safef(message, sizeof(message), "%s zoom", zoomType);
if (sameString(zoomType, ZOOM_1PT5X))
newWinWidth = winWidth/1.5;
else if (sameString(zoomType, ZOOM_3X))
newWinWidth = winWidth/3;
else if (sameString(zoomType, ZOOM_10X))
newWinWidth = winWidth/10;
else if (sameString(zoomType, ZOOM_100X))
newWinWidth = winWidth/100;
else if (sameString(zoomType, ZOOM_BASE))
newWinWidth = insideWidth/tl.mWidth;
else
errAbort("invalid zoom type %s", zoomType);
if (newWinWidth < 1)
newWinWidth = 1;
return newWinWidth;
}
static void drawScaleBar(
struct hvGfx *hvg,
MgFont *font,
int fontHeight,
int yAfterRuler,
int y,
int scaleBarTotalHeight
)
/* draws the scale bar */
{
int scaleBarPad = 2;
int scaleBarHeight = fontHeight;
// can have one for entire multi-window image
char scaleText[32];
long numBases = 0;
struct window *w;
for (w=windows; w; w=w->next)
numBases += (w->winEnd - w->winStart);
long scaleBases = computeScaleBar(numBases, scaleText, sizeof(scaleText));
int scalePixels = (int)((double)fullInsideWidth*scaleBases/numBases);
int scaleBarX = fullInsideX + (int)(((double)fullInsideWidth-scalePixels)/2);
int scaleBarEndX = scaleBarX + scalePixels;
int scaleBarY = y + 0.5 * scaleBarTotalHeight;
hvGfxTextRight(hvg, fullInsideX, y + scaleBarPad,
(scaleBarX-2)-fullInsideX, scaleBarHeight, MG_BLACK, font, scaleText);
hvGfxLine(hvg, scaleBarX, scaleBarY, scaleBarEndX, scaleBarY, MG_BLACK);
hvGfxLine(hvg, scaleBarX, y+scaleBarPad, scaleBarX,
y+scaleBarTotalHeight-scaleBarPad, MG_BLACK);
hvGfxLine(hvg, scaleBarEndX, y+scaleBarPad, scaleBarEndX,
y+scaleBarTotalHeight-scaleBarPad, MG_BLACK);
if(cartUsualBoolean(cart, BASE_SHOWASM_SCALEBAR, TRUE))
{
int fHeight = vgGetFontPixelHeight(hvg->vg, font);
hvGfxText(hvg, scaleBarEndX + 10,
y + (scaleBarTotalHeight - fHeight)/2 + ((font == mgSmallFont()) ? 1 : 0),
MG_BLACK, font, trackHubSkipHubName(database));
}
}
static int doDrawRuler(struct hvGfx *hvg, int *rulerClickHeight,
int rulerHeight, int yAfterRuler, int yAfterBases, MgFont *font,
int fontHeight, boolean rulerCds, int scaleBarTotalHeight, struct window *window)
/* draws the ruler. */
{
int titleHeight = fontHeight;
int baseHeight = fontHeight;
//int yAfterBases = yAfterRuler;
int showPosHeight = fontHeight;
int codonHeight = fontHeight;
struct dnaSeq *seq = NULL;
int rulerClickY = 0;
*rulerClickHeight = rulerHeight;
int y = rulerClickY;
hvGfxSetClip(hvg, insideX, y, insideWidth, yAfterRuler-y+1);
int relNumOff = winStart;
if (baseTitle)
{
if (window == windows) // first window, only need to do once
{
hvGfxUnclip(hvg);
hvGfxSetClip(hvg, fullInsideX, y, fullInsideWidth, yAfterRuler-y+1);
hvGfxTextCentered(hvg, fullInsideX, y, fullInsideWidth, titleHeight,MG_BLACK, font, baseTitle);
hvGfxUnclip(hvg);
hvGfxSetClip(hvg, insideX, y, insideWidth, yAfterRuler-y+1);
}
*rulerClickHeight += titleHeight;
y += titleHeight;
}
if (baseShowPos||baseShowAsm)
{
if (window == windows) // first window, only need to do once
{
hvGfxUnclip(hvg);
hvGfxSetClip(hvg, fullInsideX, y, fullInsideWidth, yAfterRuler-y+1);
char txt[256];
char numBuf[SMALLBUF];
char *freezeName = NULL;
freezeName = hFreezeFromDb(database);
sprintLongWithCommas(numBuf, virtWinEnd-virtWinStart);
if (freezeName == NULL)
freezeName = cloneString("Unknown");
if (baseShowPos&&baseShowAsm)
safef(txt,sizeof(txt),"%s %s %s (%s bp)",trackHubSkipHubName(organism),
freezeName, addCommasToPos(database, position), numBuf);
else if (baseShowPos)
safef(txt,sizeof(txt),"%s (%s bp)",addCommasToPos(database, position),numBuf);
else
safef(txt,sizeof(txt),"%s %s",trackHubSkipHubName(organism),freezeName);
freez(&freezeName);
hvGfxTextCentered(hvg, fullInsideX, y, fullInsideWidth, showPosHeight,MG_BLACK, font, txt);
hvGfxUnclip(hvg);
hvGfxSetClip(hvg, insideX, y, insideWidth, yAfterRuler-y+1);
}
*rulerClickHeight += showPosHeight;
y += showPosHeight;
}
if (baseShowScaleBar)
{
if (window == windows) // first window
{
hvGfxUnclip(hvg);
hvGfxSetClip(hvg, fullInsideX, y, fullInsideWidth, yAfterRuler-y+1);
drawScaleBar(hvg, font, fontHeight, yAfterRuler, y, scaleBarTotalHeight);
hvGfxUnclip(hvg);
hvGfxSetClip(hvg, insideX, y, insideWidth, yAfterRuler-y+1);
}
y += scaleBarTotalHeight;
*rulerClickHeight += scaleBarTotalHeight;
}
if (baseShowRuler && (insideWidth >=36))
{
hvGfxDrawRulerBumpText(hvg, insideX, y, rulerHeight, insideWidth, MG_BLACK,
font, relNumOff, winBaseCount, 0, 1);
}
if (zoomedToBaseLevel || rulerCds)
{
Color baseColor = MG_BLACK;
int start, end, chromSize;
struct dnaSeq *extraSeq;
/* extraSeq has extra leading & trailing bases
* for translation in to amino acids */
boolean complementRulerBases = cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE);
// gray bases if not matching the direction of display
if (complementRulerBases != revCmplDisp)
baseColor = MG_GRAY;
/* get sequence, with leading & trailing 3 bases
* used for amino acid translation */
start = max(winStart - 3, 0);
chromSize = hChromSize(database, chromName);
end = min(winEnd + 3, chromSize);
extraSeq = hDnaFromSeq(database, chromName, start, end, dnaUpper);
if (start != winStart - 3 || end != winEnd + 3)
{
/* at chromosome boundaries, pad with N's to assure
* leading & trailing 3 bases */
char header[4] = "NNN", trailer[4] = "NNN";
int size = winEnd - winStart + 6;
char *padded = (char *)needMem(size+1);
header[max(3 - winStart, 0)] = 0;
trailer[max(winEnd - chromSize + 3, 0)] = 0;
safef(padded, size+1, "%s%s%s", header, extraSeq->dna, trailer);
extraSeq = newDnaSeq(padded, strlen(padded), extraSeq->name);
}
/* for drawing bases, must clip off leading and trailing 3 bases */
seq = cloneDnaSeq(extraSeq);
seq = newDnaSeq(seq->dna+3, seq->size-6, seq->name);
if (zoomedToBaseLevel)
drawBases(hvg, insideX, y+rulerHeight, insideWidth, baseHeight,
baseColor, font, complementRulerBases, seq);
/* set up clickable area to toggle ruler visibility */
{
char newRulerVis[100];
safef(newRulerVis, 100, "%s=%s", RULER_TRACK_NAME,
rulerMode == tvFull ? rulerMenu[tvDense] : rulerMenu[tvFull]);
// GALT TODO should this be fullInsideX and fullInsideWidth?
mapBoxReinvoke(hvg, insideX, y+rulerHeight, insideWidth,baseHeight, NULL,
FALSE, NULL, 0, 0, "", newRulerVis);
}
if (rulerCds)
{
/* display codons */
int frame;
int firstFrame = 0;
int mod; // for determining frame ordering on display
struct simpleFeature *sfList;
double scale = scaleForWindow(insideWidth, winStart, winEnd);
/* WARNING: tricky code to assure that an amino acid
* stays in the same frame line on the browser during panning.
* There may be a simpler way... */
if (complementRulerBases)
mod = (chromSize - winEnd) % 3;
else
mod = winStart % 3;
if (mod == 0)
firstFrame = 0;
else if (mod == 1)
firstFrame = 2;
else if (mod == 2)
firstFrame = 1;
y = yAfterBases;
if (complementRulerBases)
reverseComplement(extraSeq->dna, extraSeq->size);
for (frame = 0; frame < 3; frame++, y += codonHeight)
{
/* reference frame to start of chromosome */
int refFrame = (firstFrame + frame) % 3;
/* create list of codons in the specified coding frame */
sfList = baseColorCodonsFromDna(refFrame, winStart, winEnd,
extraSeq, complementRulerBases);
/* draw the codons in the list, with alternating colors */
baseColorDrawRulerCodons(hvg, sfList, scale, insideX, y,
codonHeight, font, winStart, MAXPIXELS,
zoomedToCodonLevel);
}
}
}
hvGfxUnclip(hvg);
return y;
}
static void logTrackList(struct dyString *dy, struct track *trackList)
/* add visibile tracks to dyString, recursively called */
{
if (trackList == NULL)
return;
struct track *track;
for (track = trackList; track != NULL; track = track->next)
{
int vis = track->limitedVisSet ? track->limitedVis : track->visibility;
if (vis)
{
logTrackList(dy, track->subtracks);
if (dy->stringSize)
dyStringAppendC(dy, ',');
dyStringPrintf(dy,"%s:%d", track->track, vis);
}
}
}
static void logTrackVisibilities (char *hgsid, struct track *trackList)
/* log visibile tracks and hgsid */
{
struct dyString *dy = newDyString(1024);
// build up dyString
logTrackList(dy, trackList);
// put out ~1024 bye blocks to error_log because otherwise
// Apache will chop up the lines
char *begin = dy->string;
char *ptr = begin;
int count = 0;
for(ptr=begin; ((ptr = strchr(ptr, ',')) != NULL); ptr++)
{
if (ptr - begin > 800)
{
*ptr = 0;
fprintf(stderr, "trackLog %d %s %s %s\n", count++, database, hgsid, begin);
begin = ptr+1;
}
}
fprintf(stderr, "trackLog %d %s %s %s\n", count++, database, hgsid, begin);
dyStringFree(&dy);
}
static void rAddToTrackHash(struct hash *trackHash, struct track *trackList)
/* Add list and any children of list to hash. */
{
struct track *track;
for (track = trackList; track != NULL; track = track->next)
{
hashAddUnique(trackHash, track->track, track);
rAddToTrackHash(trackHash, track->subtracks);
}
}
struct highlightVar
// store highlight information
{
struct highlightVar *next;
char *db;
char *chrom;
long chromStart;
long chromEnd;
char *hexColor;
};
struct highlightVar *parseHighlightInfo()
// Parse highlight info from cart var to a linked list of highlightVar structs
// db.chrom:start-end#hexColor|db.chrom:start-end#hexColor|...
{
struct highlightVar *hlList = NULL;
char *highlightDef = cartOptionalString(cart, "highlight");
if(highlightDef)
{
char *hlArr[256];
int hlCount = chopByChar(cloneString(highlightDef), '|', hlArr, ArraySize(hlArr));
int i;
for (i=0; idb = cloneNextWordByDelimiter(&oneHl,'.');
h->chrom = cloneNextWordByDelimiter(&oneHl,':');
// long to handle virt chrom coordinates
h->chromStart = atol(cloneNextWordByDelimiter(&oneHl,'-'));
h->chromEnd = atol(cloneNextWordByDelimiter(&oneHl,'#'));
h->chromStart--; // Not zero based
if (highlightDef && *highlightDef != '\0')
h->hexColor = cloneString(oneHl);
slAddHead(&hlList, h);
}
slReverse(&hlList);
}
return hlList;
}
static void highlightRegions(struct cart *cart, struct hvGfx *hvg, int imagePixelHeight)
// Highlights regions in the image. Only done if theImgBox is not defined.
// Thus it is done for ps/pdf and view image but the hgTracks image is highlighted via js
{
struct highlightVar *hlList = parseHighlightInfo();
if(hlList && theImgBox == NULL) // Only highlight region when imgBox is not used. (pdf and show-image)
{
struct highlightVar *h;
for (h=hlList; h; h=h->next)
{
if (virtualSingleChrom()) // DISGUISE VMODE
{
if ((h->db && sameString(h->db, database))
&& (h->chrom && sameString(h->chrom,chromName)))
{
char position[1024];
safef(position, sizeof position, "%s:%ld-%ld", h->chrom, h->chromStart, h->chromEnd);
char *newPosition = undisguisePosition(position); // UN-DISGUISE VMODE
if (startsWith("virt:", newPosition))
{
parseVPosition(newPosition, &h->chrom, &h->chromStart, &h->chromEnd);
}
}
}
if ((h->db && sameString(h->db, database))
&& (h->chrom && sameString(h->chrom,virtChromName))
&& (h->chromEnd != 0)
&& (h->chromStart <= virtWinEnd && h->chromEnd >= virtWinStart))
{
h->chromStart = max(h->chromStart, virtWinStart);
h->chromEnd = min(h->chromEnd, virtWinEnd);
double pixelsPerBase = (double)fullInsideWidth/(virtWinEnd - virtWinStart);
int startPixels = pixelsPerBase * (h->chromStart - virtWinStart); // floor
if (startPixels < 0)
startPixels *= -1; // reverse complement
int width = pixelsPerBase * (double)(h->chromEnd - h->chromStart) + 0.5; // round up
if (width < 2)
width = 2;
// Default color to light blue, but if setting has color, use it.
unsigned int hexColor = MAKECOLOR_32(170, 255, 255);
if (h->hexColor)
{
long rgb = strtol(h->hexColor,NULL,16); // Big and little Endians
hexColor = MAKECOLOR_32( ((rgb>>16)&0xff), ((rgb>>8)&0xff), (rgb&0xff) );
}
hvGfxBox(hvg, fullInsideX + startPixels, 0, width, imagePixelHeight, hexColor);
}
}
}
}
struct hash *makeGlobalTrackHash(struct track *trackList)
/* Create a global track hash and returns a pointer to it. */
{
trackHash = newHash(8);
rAddToTrackHash(trackHash, trackList);
return trackHash;
}
//void domAddMenu(char *afterMenuId, char *newMenuId, char *label)
///* Append a new drop down menu after a given menu, by changing the DOM with jquery */
//{
//printf("$('#%s').last().after('
%s"
// "
');\n",
// afterMenuId, newMenuId, label);
//}
//
//void domAppendToMenu(char *menuId, char *url, char *label)
///* Add an entry to a drop down menu, by changing the DOM with jquery */
//{
////printf("$('#%s ul').last().after('
');\n", menuId, url, label);
//}
//void menuBarAppendExtTools()
///* printf a little javascript that adds entries to a menu */
//{
// char url[SMALLBUF];
// safef(url,ArraySize(url),"hgTracks?%s=%s&hgt.redirectTool=crispor",cartSessionVarName(), cartSessionId(cart));
// printf("\n");
//}
char *rgbColorToString(struct rgbColor color)
/* make rgbColor into printable string */
{
char buf[256];
safef(buf, sizeof buf, "rgbColor r:%d g:%d b:%d", color.r, color.g, color.b);
return cloneString(buf);
}
char *trackDumpString(struct track *track)
/* Write out info on track to string */
{
struct dyString *dy = dyStringNew(256);
dyStringPrintf(dy, "next: %lu\n", (unsigned long)track->next);
// struct track *next; /* Next on list. */
//
dyStringPrintf(dy, "track: %s\n", track->track);
//char *track; /* Track symbolic name. Name on image map etc. Same as tdb->track. */
dyStringPrintf(dy, "table: %s\n", track->table);
//char *table; /* Table symbolic name. Name of database table. Same as tdb->table.*/
dyStringPrintf(dy, "visibility: %s\n", hStringFromTv(track->visibility));
//enum trackVisibility visibility; /* How much of this want to see. */
dyStringPrintf(dy, "limitedVis: %s\n", hStringFromTv(track->limitedVis));
//enum trackVisibility limitedVis; /* How much of this actually see. */
dyStringPrintf(dy, "limitedVisSet: %d\n", track->limitedVisSet);
//boolean limitedVisSet; /* Is limited visibility set? */
dyStringPrintf(dy, "longLabel: %s\n", track->longLabel);
//char *longLabel; /* Long label to put in center. */
dyStringPrintf(dy, "shortLabel: %s\n", track->shortLabel);
//char *shortLabel; /* Short label to put on side. */
dyStringPrintf(dy, "mapsSelf: %d\n", track->mapsSelf);
//bool mapsSelf; /* True if system doesn't need to do map box. */
dyStringPrintf(dy, "drawName: %d\n", track->drawName);
//bool drawName; /* True if BED wants name drawn in box. */
dyStringPrintf(dy, "colorShades: %lu\n", (unsigned long)track->colorShades);
//Color *colorShades; /* Color scale (if any) to use. */
dyStringPrintf(dy, "color: %s\n", rgbColorToString(track->color));
//struct rgbColor color; /* Main color. */
dyStringPrintf(dy, "ixColor: %u\n", track->ixColor);
//Color ixColor; /* Index of main color. */
dyStringPrintf(dy, "altColorShades: %lu\n", (unsigned long)track->altColorShades);
//Color *altColorShades; /* optional alternate color scale */
dyStringPrintf(dy, "altColor: %s\n", rgbColorToString(track->altColor));
//struct rgbColor altColor; /* Secondary color. */
dyStringPrintf(dy, "ixAltColor: %u\n", track->ixAltColor);
//Color ixAltColor;
//void (*loadItems)(struct track *tg);
/* loadItems loads up items for the chromosome range indicated. */
dyStringPrintf(dy, "items: %lu\n", (unsigned long) track->items);
//void *items; /* Some type of slList of items. */
//char *(*itemName)(struct track *tg, void *item);
/* Return name of one of an item to display on left side. */
//char *(*mapItemName)(struct track *tg, void *item);
/* Return name to associate on map. */
//int (*totalHeight)(struct track *tg, enum trackVisibility vis);
/* Return total height. Called before and after drawItems.
* Must set the following variables. */
dyStringPrintf(dy, "height: %d\n", track->height);
//int height; /* Total height - must be set by above call. */
dyStringPrintf(dy, "lineHeight: %d\n", track->lineHeight);
//int lineHeight; /* Height per item line including border. */
dyStringPrintf(dy, "heightPer: %d\n", track->heightPer);
//int heightPer; /* Height per item line minus border. */
//int (*itemHeight)(struct track *tg, void *item);
/* Return height of one item. */
//int (*itemRightPixels)(struct track *tg, void *item);
/* Return number of pixels needed to right of item for additional labeling. (Optional) */
//void (*drawItems)(struct track *tg, int seqStart, int seqEnd,
//struct hvGfx *hvg, int xOff, int yOff, int width,
//MgFont *font, Color color, enum trackVisibility vis);
/* Draw item list, one per track. */
//void (*drawItemAt)(struct track *tg, void *item, struct hvGfx *hvg,
//int xOff, int yOff, double scale,
//MgFont *font, Color color, enum trackVisibility vis);
/* Draw a single option. This is optional, but if it's here
* then you can plug in genericDrawItems into the drawItems,
* which takes care of all sorts of things including packing. */
//int (*itemStart)(struct track *tg, void *item);
/* Return start of item in base pairs. */
//int (*itemEnd)(struct track *tg, void *item);
/* Return end of item in base pairs. */
//void (*freeItems)(struct track *tg);
/* Free item list. */
//Color (*itemColor)(struct track *tg, void *item, struct hvGfx *hvg);
/* Get color of item (optional). */
//Color (*itemNameColor)(struct track *tg, void *item, struct hvGfx *hvg);
/* Get color for the item's name (optional). */
//Color (*itemLabelColor)(struct track *tg, void *item, struct hvGfx *hvg);
/* Get color for the item's label (optional). */
//void (*mapItem)(struct track *tg, struct hvGfx *hvg, void *item,
//char *itemName, char *mapItemName, int start, int end,
//int x, int y, int width, int height);
/* Write out image mapping for a given item */
dyStringPrintf(dy, "hasUi: %d\n", track->hasUi);
//boolean hasUi; /* True if has an extended UI page. */
dyStringPrintf(dy, "wigCartData: %lu\n", (unsigned long)track->wigCartData);
//void *wigCartData; /* pointer to wigCart */
dyStringPrintf(dy, "extraUiData: %lu\n", (unsigned long)track->extraUiData);
//void *extraUiData; /* Pointer for track specific filter etc. data. */
//void (*trackFilter)(struct track *tg);
/* Stuff to handle user interface parts. */
dyStringPrintf(dy, "customPt: %lu\n", (unsigned long)track->customPt);
//void *customPt; /* Misc pointer variable unique to track. */
dyStringPrintf(dy, "customInt: %d\n", track->customInt);
//int customInt; /* Misc int variable unique to track. */
dyStringPrintf(dy, "subType: %d\n", track->subType);
//int subType; /* Variable to say what subtype this is for similar tracks to share code. */
/* Stuff for the various wig incarnations - sample, wig, bigWig */
dyStringPrintf(dy, "minRange: %.2f, maxRange: %.2f\n", track->minRange, track->maxRange);
//float minRange, maxRange; /*min and max range for sample tracks 0.0 to 1000.0*/
dyStringPrintf(dy, "scaleRange: %.2f\n", track->scaleRange);
//float scaleRange; /* What to scale samples by to get logical 0-1 */
dyStringPrintf(dy, "graphUpperLimit: %e, grapLowerLimit: %e\n", track->graphUpperLimit, track->graphLowerLimit);
//double graphUpperLimit, graphLowerLimit; /* Limits of actual data in window for wigs. */
dyStringPrintf(dy, "preDrawContainer: %lu\n", (unsigned long)track->preDrawContainer);
//struct preDrawContainer *preDrawContainer; /* Numbers to graph in wig, one per pixel */
//struct preDrawContainer *(*loadPreDraw)(struct track *tg, int seqStart, int seqEnd, int width);
dyStringPrintf(dy, "wigGraphOutput: %lu\n", (unsigned long)track->wigGraphOutput);
//struct wigGraphOutput *wigGraphOutput; /* Where to draw wig - different for transparency */
/* Do bits that load the predraw buffer. Called to set preDrawContainer */
dyStringPrintf(dy, "bbiFile: %lu\n", (unsigned long)track->bbiFile);
//struct bbiFile *bbiFile; /* Associated bbiFile for bigWig or bigBed. */
dyStringPrintf(dy, "bedSize: %d\n", track->bedSize);
//int bedSize; /* Number of fields if a bed file. */
dyStringPrintf(dy, "isBigBed: %d\n", track->isBigBed);
//boolean isBigBed; /* If a bed, is it a bigBed? */
dyStringPrintf(dy, "isRemoteSql: %d\n", track->isRemoteSql);
//boolean isRemoteSql; /* Is using a remote mySQL connection. */
dyStringPrintf(dy, "remoteSqlHost: %s\n", track->remoteSqlHost);
//char *remoteSqlHost; /* Host machine name for remote DB. */
dyStringPrintf(dy, "remoteSqlUser: %s\n", track->remoteSqlUser);
//char *remoteSqlUser; /* User name for remote DB. */
dyStringPrintf(dy, "remoteSqlPassword: %s\n", track->remoteSqlPassword);
//char *remoteSqlPassword; /* Password for remote DB. */
dyStringPrintf(dy, "remoteSqlDatabase: %s\n", track->remoteSqlDatabase);
//char *remoteSqlDatabase; /* Database in remote DB. */
dyStringPrintf(dy, "remoteSqlTable: %s\n", track->remoteSqlTable);
//char *remoteSqlTable; /* Table name in remote DB. */
dyStringPrintf(dy, "otherDb: %s\n", track->otherDb);
//char *otherDb; /* Other database for an axt track. */
dyStringPrintf(dy, "private: %d\n", track->private);
//unsigned short private; /* True(1) if private, false(0) otherwise. */
dyStringPrintf(dy, "priority: %.2f\n", track->priority);
//float priority; /* Tracks are drawn in priority order. */
dyStringPrintf(dy, "defaultPriority: %.2f\n", track->defaultPriority);
//float defaultPriority; /* Tracks are drawn in priority order. */
dyStringPrintf(dy, "groupName: %s\n", track->groupName);
//char *groupName; /* Name of group if any. */
dyStringPrintf(dy, "group: %lu\n", (unsigned long)track->group);
//struct group *group; /* Group this track is associated with. */
dyStringPrintf(dy, "defaultGroupName: %s\n", track->defaultGroupName);
//char *defaultGroupName; /* default Group this track is associated with. */
dyStringPrintf(dy, "canPack: %d\n", track->canPack);
//boolean canPack; /* Can we pack the display for this track? */
dyStringPrintf(dy, "spaceSaver: %lu\n", (unsigned long)track->ss);
//struct spaceSaver *ss; /* Layout when packed. */
dyStringPrintf(dy, "tdb: %lu\n", (unsigned long)track->tdb);
//struct trackDb *tdb; /*todo:change visibility, etc. to use this */
// ADDED by GALT
if (track->tdb)
dyStringPrintf(dy, "tdb settings:\n%s\n", track->tdb->settings);
dyStringPrintf(dy, "expScale: %.2f\n", track->expScale);
//float expScale; /* What to scale expression tracks by. */
dyStringPrintf(dy, "expTable: %s\n", track->expTable);
//char *expTable; /* Expression table in hgFixed. */
// factorSource
dyStringPrintf(dy, "sourceCount: %d\n", track->sourceCount);
//int sourceCount; /* Number of sources for factorSource tracks. */
dyStringPrintf(dy, "sources: %lu\n", (unsigned long)track->sources);
//struct expRecord **sources; /* Array of sources */
dyStringPrintf(dy, "sourceRightPixels: %d\n", track->sourceRightPixels);
//int sourceRightPixels; /* Number of pixels to right we'll need. */
// exon/Next
dyStringPrintf(dy, "exonArrows: %d\n", track->exonArrows);
//boolean exonArrows; /* Draw arrows on exons? */
dyStringPrintf(dy, "exonArrowsAlways: %d\n", track->exonArrowsAlways);
//boolean exonArrowsAlways; /* Draw arrows on exons even with introns showing? */
dyStringPrintf(dy, "nextExonButtonable: %d\n", track->nextExonButtonable);
//boolean nextExonButtonable; /* Use the next-exon buttons? */
dyStringPrintf(dy, "nextItemButtonable: %d\n", track->nextItemButtonable);
//boolean nextItemButtonable; /* Use the next-gene buttons? */
dyStringPrintf(dy, "itemAttrTbl: %lu\n", (unsigned long)track->itemAttrTbl);
//struct itemAttrTbl *itemAttrTbl; /* relational attributes for specific items (color) */
/* fill in left label drawing area */
dyStringPrintf(dy, "labelColor: %u\n", track->labelColor);
//Color labelColor; /* Fixed color for the track label (optional) */
//void (*drawLeftLabels)(struct track *tg, int seqStart, int seqEnd,
// struct hvGfx *hvg, int xOff, int yOff, int width, int height,
// boolean withCenterLabels, MgFont *font,
// Color color, enum trackVisibility vis);
dyStringPrintf(dy, "subtracks: %lu\n", (unsigned long)track->subtracks);
//struct track *subtracks; /* list of subsidiary tracks that are
//loaded and drawn by this track. This
//is used for "composite" tracks, such
//as "mafWiggle */
dyStringPrintf(dy, "parent: %lu\n", (unsigned long)track->parent);
//struct track *parent; /* Parent track if any */
dyStringPrintf(dy, "prevTrack: %lu\n", (unsigned long)track->prevTrack);
//struct track *prevTrack; // if not NULL, points to track immediately above in the image.
// Needed by ConditionalCenterLabel logic
//void (*nextPrevExon)(struct track *tg, struct hvGfx *hvg, void *item, int x, int y, int w, int h, boolean next);
/* Function will draw the button on a track item and assign a map */
/* box to it as well, so that a click will move the browser window */
/* to the next (or previous if next==FALSE) item. This is meant to */
/* jump to parts of an item already partially in the window but is */
/* hanging off the edge... e.g. the next exon in a gene. */
//void (*nextPrevItem)(struct track *tg, boolean next);
/* If this function is given, it can dictate where the browser loads */
/* up based on whether a next-item button on the longLabel line of */
/* the track was pressed (as opposed to the next-item buttons on the */
/* track items themselves... see nextPrevExon() ). This is meant for */
/* going to the next/previous item currently unseen in the browser, */
/* e.g. the next gene. SO FAR THIS IS UNIMPLEMENTED. */
//char *(*itemDataName)(struct track *tg, char *itemName);
/* If not NULL, function to translated an itemName into a data name.
* This is can be used for looking up sequence, CDS, etc. It is used
* to support item names that have uniqueness identifiers added to deal
* with multiple alignments. The resulting value should *not* be freed,
* and it should be assumed that it might only remain valid for a short
* period of time.*/
dyStringPrintf(dy, "loadTime: %d\n", track->loadTime);
//int loadTime; /* Time it takes to load (for performance tuning) */
dyStringPrintf(dy, "drawTime: %d\n", track->drawTime);
//int drawTime; /* Time it takes to draw (for performance tuning) */
dyStringPrintf(dy, "remoteDataSource: %d\n", track->remoteDataSource);
//enum enumBool remoteDataSource; /* The data for this track is from a remote source */
/* Slow retrieval means image can be rendered via an AJAX callback. */
dyStringPrintf(dy, "customTrack: %d\n", track->customTrack);
//boolean customTrack; /* Need to explicitly declare this is a custom track */
dyStringPrintf(dy, "syncChildVisToSelf: %d\n", track->syncChildVisToSelf);
//boolean syncChildVisToSelf; /* If TRUE sync visibility to of children to self. */
dyStringPrintf(dy, "networkErrMsg: %s\n", track->networkErrMsg);
//char *networkErrMsg; /* Network layer error message */
dyStringPrintf(dy, "parallelLoading: %d\n", track->parallelLoading);
//boolean parallelLoading; /* If loading in parallel, usually network resources. */
dyStringPrintf(dy, "summary: %lu\n", (unsigned long) track->summary);
//struct bbiSummaryElement *summary; /* for bigBed */
dyStringPrintf(dy, "summAll: %lu\n", (unsigned long) track->sumAll);
//struct bbiSummaryElement *sumAll; /* for bigBed */
dyStringPrintf(dy, "drawLabelInBox: %d\n", track->drawLabelInBox);
//boolean drawLabelInBox; /* draw labels into the features instead of next to them */
//};
return dyStringCannibalize(&dy);
}
char *makeDumpURL(char *text)
/* Make a temp file to hold big dump. Return URL to it. */
{
// trackDump output is quite large
struct tempName trkDmp;
trashDirFile(&trkDmp, "hgt", "hgt", ".txt");
FILE *f = mustOpen(trkDmp.forCgi, "w");
fprintf(f, "%s", text);
carefulClose(&f);
return cloneString(trkDmp.forHtml);
}
char *makeTrackDumpLink(struct track *track)
/* Make a track dump to trash, and return html link to it */
{
char *tds = trackDumpString(track);
char *url = makeDumpURL(tds);
char buf[1024];
safef(buf, sizeof buf, "URL", url);
freeMem(tds);
freeMem(url);
return cloneString(buf);
}
boolean regionsAreInOrder(struct virtRegion *virtRegion1, struct virtRegion *virtRegion2)
/* Return TRUE if the regions are on the same chrom and non-overlapping
* and are in-order, i.e. region 1 appears before region2. */
{
if (sameString(virtRegion1->chrom, virtRegion2->chrom) && virtRegion1->end <= virtRegion2->start)
return TRUE;
return FALSE;
}
/* --- Virtual Chromosome Functions --- */
boolean virtualSingleChrom()
/* Return TRUE if using virtual single chromosome mode */
{
return (sameString(virtModeType,"exonMostly") || sameString(virtModeType,"geneMostly"));
}
void parseVPosition(char *position, char **pChrom, long *pStart, long *pEnd)
/* parse Virt position */
{
if (!position)
{
errAbort("position NULL");
}
char *vPos = cloneString(position);
stripChar(vPos, ',');
char *colon = strchr(vPos, ':');
if (!colon)
errAbort("position has no colon");
char *dash = strchr(vPos, '-');
if (!dash)
errAbort("position has no dash");
*colon = 0;
*dash = 0;
*pChrom = cloneString(vPos);
*pStart = atol(colon+1) - 1;
*pEnd = atol(dash+1);
}
void parseNVPosition(char *position, char **pChrom, int *pStart, int *pEnd)
/* parse NonVirt position */
{
if (!position)
{
errAbort("position NULL");
}
char *vPos = cloneString(position);
stripChar(vPos, ',');
char *colon = strchr(vPos, ':');
if (!colon)
errAbort("position has no colon");
char *dash = strchr(vPos, '-');
if (!dash)
errAbort("position has no dash");
*colon = 0;
*dash = 0;
*pChrom = cloneString(vPos);
*pStart = atoi(colon+1) - 1;
*pEnd = atoi(dash+1);
}
char *disguisePositionVirtSingleChrom(char *position) // DISGUISE VMODE
/* Hide the virt position, convert to real single chrom span.
* position should be virt chrom span.
* Can handle anything in the virt single chrom. */
{
/* parse Virt position */
char *chrom = NULL;
long start = 0;
long end = 0;
parseVPosition(position, &chrom, &start, &end);
if (!sameString(chrom, "virt"))
return position; // return original
struct window *windows = makeWindowListFromVirtChrom(start, end);
char *nonVirtChromName = windows->chromName;
if (!sameString(nonVirtChromName, chromName))
return position; // return original
int nonVirtWinStart = windows->winStart;
int nonVirtWinEnd = windows->winEnd;
struct window *w;
for (w=windows->next; w; w=w->next)
{
if (!sameString(w->chromName, nonVirtChromName))
return position; // return original
if (w->winEnd < nonVirtWinEnd)
return position; // return original
nonVirtWinEnd = w->winEnd;
}
char nvPos[256];
safef(nvPos, sizeof nvPos, "%s:%d-%d", nonVirtChromName, nonVirtWinStart+1, nonVirtWinEnd);
slFreeList(&windows);
return cloneString(nvPos);
}
char *undisguisePosition(char *position) // UN-DISGUISE VMODE
/* Find the virt position
* position should be real chrom span.
* Limitation: can only convert things in the current windows set. */
{
/* parse NonVirt position */
char *chrom = NULL;
int start = 0;
int end = 0;
parseNVPosition(position, &chrom, &start, &end);
if (!sameString(chrom, chromName))
return position; // return original
long newStart = -1;
long newEnd = -1;
struct window *lastW = NULL;
struct window *w = NULL;
for (w = windows; w; w=w->next)
{
// double check chrom is same thoughout all windows, otherwise warning, return original value
if (!sameString(w->chromName, chromName))
{
return position; // return original
}
// check that the regions are ascending and non-overlapping
if (lastW && w->winStart < lastW->winEnd)
{
return position; // return original
}
// overlap with position?
// if intersection,
if (w->winEnd > start && end > w->winStart)
{
int s = max(start, w->winStart);
int e = min(end, w->winEnd);
long cs = s - w->winStart + w->virtStart;
long ce = e - w->winStart + w->virtStart;
if (newStart == -1)
newStart = cs;
newEnd = ce;
}
lastW = w;
}
if (newStart == -1) // none of the windows intersected with the position
return position; // return original
// return new virt undisguised position as a string
char newPos[1024];
safef (newPos, sizeof newPos, "virt:%ld-%ld", (newStart+1), newEnd);
return cloneString(newPos);
}
char *windowsSpanPosition()
/* Return a position string that spans all the windows.
* Windows should be on same chrom and ascending and non-overlapping.*/
{
char buf[256];
char *chromName = windows->chromName;
int start = windows->winStart;
struct window *w = windows, *last = NULL;
while(w->next) // find last window
{
last = w;
w = w->next;
if (!sameString(chromName, w->chromName))
errAbort("windowsSpanPosition: expected all windows to be on the same chrom but found %s and %s", chromName, w->chromName);
if (w->winStart < last->winEnd)
errAbort("windowsSpanPosition: expected all windows to be ascending non-overlapping, found %d < %d", w->winStart, last->winEnd);
}
int end = w->winEnd;
safef(buf, sizeof buf, "%s:%d-%d", chromName, start+1, end);
return cloneString(buf);
}
void padVirtRegions(int windowPadding)
/* Pad virt regions with windowPadding bases
*
* NOTE a simple padding would not worry about merging or order. Just expand the beginning and end of each region.
* NOTE this assumes that the regions are in order, but tolerates hiccups in order.
* DONE make it handle multiple chromosomes
*
* TODO what about just modifying the original list directly?
* I do not know if this is handling merging correctly.
* DONE Maybe I should just add the padding directly into the exon-fetch-merge code.
* I have looked at that earlier, and it should work easily.
* It might also have the advantage of not having to create a duplicate list?
*
* TODO how do I test that the output is correct.
* if the input has ordered non-duplicate regions, then the output should be likewise.
* */
{
int regionCount = 0;
long regionBases=0;
struct virtRegion *virtRegion, *lastVirtRegion = NULL;
int leftWindowPadding = 0;
int rightWindowPadding = 0;
struct virtRegion *v, *newList = NULL;
char *lastChrom = NULL;
int chromSize = -1;
for(virtRegion=virtRegionList; virtRegion; virtRegion = virtRegion->next)
{
AllocVar(v);
if (!sameOk(virtRegion->chrom, lastChrom))
{
chromSize = hChromSize(database, virtRegion->chrom);
}
v->chrom = virtRegion->chrom; // TODO is cloning the string needed?
leftWindowPadding = windowPadding;
rightWindowPadding = windowPadding;
if (lastVirtRegion && regionsAreInOrder(lastVirtRegion, virtRegion))
{
int distToPrevRegion = virtRegion->start - lastVirtRegion->end;
if (distToPrevRegion < (2*windowPadding))
{
leftWindowPadding = distToPrevRegion/2;
}
}
if (virtRegion->next && regionsAreInOrder(virtRegion, virtRegion->next))
{
int distToNextRegion = virtRegion->next->start - virtRegion->end;
if (distToNextRegion < (2*windowPadding))
{
rightWindowPadding = (distToNextRegion+1)/2;
// +1 to balance for odd number of bases between, arbitrarily adding it to right side
}
}
v->start = virtRegion->start - leftWindowPadding;
v->end = virtRegion->end + rightWindowPadding;
if (v->start < 0)
v->start = 0;
if (v->end >= chromSize)
v->end = chromSize;
regionBases += (v->end - v->start);
slAddHead(&newList, v);
lastVirtRegion = virtRegion;
lastChrom = virtRegion->chrom;
++regionCount;
}
slReverse(&newList);
virtRegionList = newList; // update new list --
// TODO should the old one be freed? if so, the chrom name should use cloneString
}
void makeVirtChrom()
/* build virtual chrom array from virt region list */
{
virtRegionCount = slCount(virtRegionList);
AllocArray(virtChrom, virtRegionCount);
struct virtRegion *v;
int i = 0;
long totalBases = 0;
for(v=virtRegionList;v;v=v->next,++i)
{
virtChrom[i].virtPos = totalBases;
virtChrom[i].virtRegion = v;
totalBases += (v->end - v->start);
}
virtSeqBaseCount = totalBases;
}
void virtChromBinarySearch(long target, long *resultIndex, int *resultOffset)
/* Do a binary search for the target position in the virtual chrom.
* Return virtChrom Index and Offset if found.
* Return -1 if target out of range. TODO maybe change to errAbort*/
{
//The binary search will either return match or out-of-range.
long index = -1;
int offset = -1;
long a = 0; // start of the array
long b = virtRegionCount - 1; // end of array where N = count of regions
if (target >=0 && target < virtSeqBaseCount)
{
while(1)
{
long c = (a + b) / 2;
if (a > b)
{
index = b;
break;
}
else if (target == virtChrom[c].virtPos)
{
// (exact match)
index = c;
break;
}
else if (target > virtChrom[c].virtPos)
{
a = c + 1;
}
else if (target < virtChrom[c].virtPos)
{
b = c - 1;
}
else
{
// probably should not happen
errAbort("Unexpected outcome in virtChromBinarySearch.");
}
}
}
else if (target == virtSeqBaseCount)
{ // tolerate this special case
index = virtRegionCount - 1;
}
if (index >= 0)
offset = target - virtChrom[index].virtPos;
*resultIndex = index;
*resultOffset = offset;
}
void testVirtChromBinarySearch()
/* test virt chrom binary search */
{
warn("about to test virt chrom binary search: virtSeqBaseCount=%ld virtRegionCount=%d", virtSeqBaseCount, virtRegionCount);
long i;
int o;
long target = -1;
virtChromBinarySearch(target, &i, &o);
if (i != -1)
errAbort("target %ld result %ld %d ", target, i, o);
for(target=0; target < virtSeqBaseCount; ++target)
{
virtChromBinarySearch(target, &i, &o);
if (virtChrom[i].virtPos + o != target)
errAbort("target = %ld result %ld %d ", target, i, o);
struct virtRegion *v = virtChrom[i].virtRegion;
int size = v->end - v->start;
if (o > size)
errAbort("target = %ld result %ld %d > size=%d", target, i, o, size);
}
target = virtSeqBaseCount;
virtChromBinarySearch(target, &i, &o);
if (i != virtRegionCount - 1)
errAbort("target = virtSeqBaseCount = %ld result %ld %d ", target, i, o);
target = virtSeqBaseCount+1;
virtChromBinarySearch(target, &i, &o);
if (i != -1)
errAbort("target = virtSeqBaseCount = %ld result %ld %d ", target, i, o);
warn("Got past test virt chrom binary search");
}
struct window *makeWindowListFromVirtChrom(long virtWinStart, long virtWinEnd)
/* make list of windows from virtual position on virtualChrom */
{
// quick check of virt win start and end
if (virtWinEnd == virtWinStart)
return NULL;
if (virtWinStart < 0 || virtWinStart >= virtSeqBaseCount)
errAbort("unexpected error. virtWinStart=%ld out of range. virtSeqBaseCount=%ld", virtWinStart, virtSeqBaseCount);
if (virtWinEnd < 0 || virtWinEnd > virtSeqBaseCount)
errAbort("unexpected error. virtWinEnd=%ld out of range. virtSeqBaseCount=%ld", virtWinEnd, virtSeqBaseCount);
if (virtWinEnd - virtWinStart < 1)
errAbort("unexpected error. virtual window size < 1 base. virtWinStart=%ld virtWinEnd=%ld virtSeqBaseCount=%ld", virtWinStart, virtWinEnd, virtSeqBaseCount);
long virtIndexStart;
int virtOffsetStart;
virtChromBinarySearch(virtWinStart, &virtIndexStart, &virtOffsetStart);
if (virtIndexStart == -1)
errAbort("unexpected failure to find target virtWinStart %ld in virtChrom Array", virtWinStart);
long virtIndexEnd;
int virtOffsetEnd;
virtChromBinarySearch(virtWinEnd, &virtIndexEnd, &virtOffsetEnd);
if (virtIndexEnd == -1)
errAbort("unexpected failure to find target virtWinEnd %ld in virtChrom Array", virtWinEnd);
// create new windows list from virt chrom
struct window *windows = NULL;
long i = virtIndexStart;
int winCount = 0;
long basesInWindows = 0; // TODO not actually using this variable
for(i=virtIndexStart; i <= virtIndexEnd; ++i)
{
struct window *w;
AllocVar(w);
w->organism = organism; // obsolete
w->database = database; // obsolete
struct virtRegion *v = virtChrom[i].virtRegion;
long virtPos = virtChrom[i].virtPos;
w->chromName = v->chrom;
if (i == virtIndexStart)
{
w->winStart = v->start + virtOffsetStart;
w->virtStart = virtPos + virtOffsetStart;
}
else
{
w->winStart = v->start;
w->virtStart = virtPos;
}
if (i == virtIndexEnd)
{
if (virtOffsetEnd == 0)
continue;
w->winEnd = v->start + virtOffsetEnd;
}
else
{
w->winEnd = v->end;
}
w->virtEnd = w->virtStart + (w->winEnd - w->winStart);
w->regionOdd = i % 2;
basesInWindows += (w->winEnd - w->winStart);
slAddHead(&windows, w);
++winCount;
}
slReverse(&windows);
if (basesInWindows != (virtWinEnd-virtWinStart)) // was virtWinBaseCount but now I call this routine for highlight pos as well as virt pos.
errAbort("makeWindowListFromVirtChrom: unexpected error basesInWindows(%ld) != virtWinBaseCount(%ld)", basesInWindows, virtWinEnd-virtWinStart);
return windows;
}
char *nonVirtPositionFromWindows()
/* Must have created the virtual chrom first.
* Currently just a hack to use windows,
* use the (first) window(s) from that to get real chrom name start end */
{
// assumes makeWindowListFromVirtChrom() has already been called.
if (!windows)
errAbort("nonVirtPositionFromWindows() unexpected error, windows list not initialized yet");
char *nonVirtChromName = windows->chromName;
int nonVirtWinStart = windows->winStart;
int nonVirtWinEnd = windows->winEnd;
// Extending this through more of the windows,
// if it is on the same chrom and maybe not too far separated.
struct window *w;
for (w=windows->next; w; w=w->next)
{
if (sameString(w->chromName, nonVirtChromName) && w->winEnd > nonVirtWinEnd)
nonVirtWinEnd = w->winEnd;
}
// TODO Also consider preserving the original bases in windows width (with clipping).
char nvPos[256];
safef(nvPos, sizeof nvPos, "%s:%d-%d", nonVirtChromName, nonVirtWinStart+1, nonVirtWinEnd);
return cloneString(nvPos);
}
char *nonVirtPositionFromHighlightPos()
/* Must have created the virtual chrom first.
* Currently just a hack to use windows,
* use the (first) window(s) from that to get real chrom name start end */
{
struct highlightVar *h = parseHighlightInfo();
if (!(h && h->db && sameString(h->db, database) && sameString(h->chrom,"virt")))
return NULL;
struct window *windows = makeWindowListFromVirtChrom(h->chromStart, h->chromEnd);
char *nonVirtChromName = windows->chromName;
int nonVirtWinStart = windows->winStart;
int nonVirtWinEnd = windows->winEnd;
// Extending this through more of the windows,
// if it is on the same chrom and maybe not too far separated.
struct window *w;
for (w=windows->next; w; w=w->next)
{
if (sameString(w->chromName, nonVirtChromName) && w->winEnd > nonVirtWinEnd)
nonVirtWinEnd = w->winEnd;
}
// TODO Also consider preserving the original bases in windows width (with clipping).
char nvPos[256];
safef(nvPos, sizeof nvPos, "%s.%s:%d-%d#%s", h->db, nonVirtChromName, nonVirtWinStart+1, nonVirtWinEnd, h->hexColor);
return cloneString(nvPos);
}
void allocPixelsToWindows()
/* Allocate pixels to windows, sets insideWidth and insideX
*
* TODO currently uses a strategy that places a window at a pixel location
* directly, because of round-off and missing small windows this can occasionally
* lead to gaps not covered by any pixel. Consider replacing it with something
* that tries not to leave many gaps -- but how to do it without distortion of some window sizes?
* */
{
double pixelsPerBase = (double)fullInsideWidth / virtWinBaseCount;
long basesUsed = 0;
int windowsTooSmall = 0;
struct window **pWindows = &windows;
struct window *window;
int winCount = slCount(windows);
for(window=windows;window;window=window->next)
{
int basesInWindow = window->winEnd - window->winStart;
int pixelsInWindow = 0.5 + (double)basesInWindow * pixelsPerBase; // should this round up ? + 0.5?
window->insideWidth = pixelsInWindow;
window->insideX = fullInsideX + basesUsed * pixelsPerBase;
basesUsed += basesInWindow;
if (pixelsInWindow < 1) // remove windows less than one pixel from the list
{
*pWindows = window->next;
--winCount;
++windowsTooSmall;
}
else
{
pWindows = &window->next;
}
}
}
struct positionMatch *virtChromSearchForPosition(char *chrom, int start, int end, boolean findNearest)
/* Search the virtual chrom for the query chrom, start, end position
*
* TODO GALT: Intially this can be a simple brute-force search of the entire virtChrom array.
*
* However, this will need to be upgraded to using a rangeTree or similar structure
* to rapidly return multiple regions that overlap the query position.
* */
{
struct positionMatch *list=NULL, *p;
int nearestRegion = -1;
boolean nearestAfter = TRUE;
int nearestDistance = INT_MAX;
int i;
struct virtRegion *v = NULL;
long virtPos = 0;
for(i=0; i < virtRegionCount; ++i)
{
v = virtChrom[i].virtRegion;
virtPos = virtChrom[i].virtPos;
if (sameString(v->chrom,chrom))
{ // TODO will we need to support finding closest misses too?
if (v->end > start && end > v->start) // overlap
{
int s = max(start, v->start);
int e = min(end , v->end );
AllocVar(p);
p->virtStart = virtPos + (s - v->start);
p->virtEnd = virtPos + (e - v->start);
slAddHead(&list, p);
}
else if (findNearest && (!list))
{
int thisDist = v->start - start;
boolean thisAfter = TRUE;
if (thisDist < 0) // absolute value
{
thisDist = -thisDist;
thisAfter = FALSE;
}
if (thisDist < nearestDistance)
{
nearestRegion = i;
nearestDistance = thisDist;
nearestAfter = thisAfter;
}
}
}
}
if (findNearest && (!list) && nearestRegion != -1)
{
i = nearestRegion;
v = virtChrom[i].virtRegion;
virtPos = virtChrom[i].virtPos;
AllocVar(p);
if (nearestAfter)
{
p->virtStart = virtPos;
p->virtEnd = p->virtStart + (end - start);
}
else
{
p->virtEnd = virtPos + (v->end - v->start);
p->virtStart = p->virtEnd - (end - start);
}
if (p->virtEnd > virtSeqBaseCount)
p->virtEnd = virtSeqBaseCount;
slAddHead(&list, p);
}
slReverse(&list);
return list;
}
int matchVPosCompare(const void *elem1, const void *elem2)
/* compare elements based on vPos */
{
const struct positionMatch *a = *((struct positionMatch **)elem1);
const struct positionMatch *b = *((struct positionMatch **)elem2);
if (a->virtStart == b->virtStart)
errAbort("matchVPosCompare error should not happen: 2 elements being sorted have the same virtStart=%ld", a->virtStart);
else if (a->virtStart > b->virtStart)
return 1;
return -1;
}
void matchSortOnVPos(struct positionMatch **pList)
/* Sort positions by virtPos
* pList will be sorted by chrom, start, end but we want it ordered by vPos
*/
{
slSort(pList, matchVPosCompare);
}
struct positionMatch *matchMergeContiguousVPos(struct positionMatch *list)
/* Merge contiguous matches spanning multiple touching windows */
{
struct positionMatch *newList = NULL;
struct positionMatch *m;
long lastStart = -1;
long lastEnd = -1;
struct positionMatch *lastM = NULL;
boolean inMerge = FALSE;
long mergeStart = -1;
long mergeEnd = -1;
if (!list)
return NULL;
for(m=list; 1; m=m->next)
// special loop condition allows it to stop AFTER it goes thru the loop once as m=NULL.
// this flushes out the last value.
{
if (inMerge)
{
if (m && m->virtStart == lastEnd)
{
// continue merging, do nothing.
// maybe could be freeing a skipped node here.
}
else
{
inMerge = FALSE;
mergeEnd = lastEnd;
// create new merged node and add it to new list
struct positionMatch *n;
AllocVar(n);
n->virtStart = mergeStart;
n->virtEnd = mergeEnd;
slAddHead(&newList, n);
}
}
else
{
if (m && m->virtStart == lastEnd)
{
inMerge = TRUE;
mergeStart = lastStart;
}
else if (lastM) // just transfer the last node unmodified to the new list.
{
slAddHead(&newList, lastM);
}
}
if (!m)
break;
lastStart = m->virtStart;
lastEnd = m->virtEnd;
lastM = m;
}
slReverse(&newList);
return newList;
}
// ----------------------------------
void initVirtRegionsFromSOD1Hardwired()
/* create a testing regionlist from SOD1 (uc002ypa.3) hardwired */
{
virtRegionList = NULL;
char *chrom="chr21";
//int exonStarts[] = {33031934,33036102,33038761,33039570,33040783};
//int exonEnds[] = {33032154,33036199,33038831,33039688,33041243};
// BASE-level zoom in two 20 bp exons and their on-screen junction
int exonStarts[] = {33032134,33036102};
int exonEnds[] = {33032154,33036122};
int i;
for(i=0; ichrom = cloneString(chrom);
v->start = exonStarts[i];
v->end = exonEnds[i];
v->strand[0] = '+';
v->strand[1] = 0;
slAddHead(&virtRegionList, v);
}
slReverse(&virtRegionList);
}
void initVirtRegionsFromEmGeneTable(char *ucscTranscriptId)
/* create a testing regionlist from knownGene transcript with given id */
{
struct sqlConnection *conn = hAllocConn(database);
virtRegionList = NULL;
struct sqlResult *sr;
char **row;
int rowOffset = 0;
if (hIsBinned(database, emGeneTable)) // skip first bin column if any
++rowOffset;
char query[256];
sqlSafef(query, sizeof(query), "select * from %s where name='%s'", emGeneTable, ucscTranscriptId);
sr = sqlGetResult(conn, query);
while ((row = sqlNextRow(sr)) != NULL)
{
struct genePred *gene = genePredLoad(row+rowOffset);
int i;
for(i=0; i< gene->exonCount; ++i)
{
struct virtRegion *v;
AllocVar(v);
v->chrom = cloneString(gene->chrom);
v->start = gene->exonStarts[i];
v->end = gene->exonEnds[i];
v->strand[0] = gene->strand[0];
v->strand[1] = 0;
slAddHead(&virtRegionList, v);
}
genePredFree(&gene);
}
sqlFreeResult(&sr);
slReverse(&virtRegionList);
hFreeConn(&conn);
}
boolean initSingleAltHaplotype(char *haplotypeId)
/* create a testing regionlist from haplotype with given id */
{
struct sqlConnection *conn = hAllocConn(database);
if (!virtRegionList) // this should already contain allchroms.
errAbort("unexpected error in initSingleAltHaplotype: virtRegionList is NULL, should contain all chroms");
struct virtRegion *after = virtRegionList;
virtRegionList = NULL;
struct sqlResult *sr;
char **row;
char *table = endsWith(haplotypeId, "_fix") ? "fixLocations" : "altLocations";
if (! hTableExists(database, table))
{
warn("initSingleAltHaplotype: table '%s' not found in database %s, "
"can't find %s", table, database, haplotypeId);
return FALSE;
}
// where is the alt haplo placed?
char query[256];
sqlSafef(query, sizeof(query), "select chrom, chromStart, chromEnd from %s "
"where name rlike '^%s(:[0-9-]+)?'", table, haplotypeId);
sr = sqlGetResult(conn, query);
row = sqlNextRow(sr);
if (!row)
{
warn("no haplotype found for [%s] in %s", haplotypeId, table);
return FALSE;
}
char *haploChrom = cloneString(row[0]);
int haploStart = sqlUnsigned(row[1]);
int haploEnd = sqlUnsigned(row[2]);
sqlFreeResult(&sr);
// what is the size of the alt haplo?
int haploSize = hChromSize(database, haplotypeId); // hopefully this will work
// insert into list replacing original haploChrom record
struct virtRegion *before = NULL;
boolean found = FALSE;
long offset = 0;
struct virtRegion *v;
while ((v=slPopHead(&after)))
{
if (sameString(haploChrom, v->chrom))
{
found = TRUE;
break;
}
offset += (v->end - v->start);
slAddHead(&before, v);
}
if (!found)
{
warn("initSingleAltHaplotype: chrom %s on which alt %s is placed is not found in all chroms list!", haploChrom, haplotypeId);
return FALSE;
}
slReverse(&before);
// for now, make virtchrom with just one chrom plus its haplo in the middle
defaultVirtWinStart = 0;
defaultVirtWinEnd = 0;;
AllocVar(v);
v->chrom = haploChrom;
v->start = 0;
v->end = haploStart;
defaultVirtWinStart = v->end - haploSize;
if (defaultVirtWinStart < 0)
defaultVirtWinStart = 0;
defaultVirtWinEnd = v->end;
slAddHead(&virtRegionList, v);
AllocVar(v);
v->chrom = haplotypeId;
v->start = 0;
v->end = haploSize;
defaultVirtWinEnd += haploSize;
slAddHead(&virtRegionList, v);
AllocVar(v);
v->chrom = haploChrom;
v->start = haploEnd;
int chromSize = hChromSize(database, haploChrom); // size of the regular chrom
v->end = chromSize;
defaultVirtWinEnd += haploSize;
if (defaultVirtWinEnd > chromSize)
defaultVirtWinEnd = chromSize;
slAddHead(&virtRegionList, v);
slReverse(&virtRegionList);
hFreeConn(&conn);
defaultVirtWinStart += offset;
defaultVirtWinEnd += offset;
// concatenate the 3 lists.
virtRegionList = slCat(before,slCat(virtRegionList,after));
return TRUE;
}
void initVirtRegionsFromKnownCanonicalGenes(char *table)
// OBSOLETED by initVirtRegionsFromEMGeneTableExons()
/* Create a regionlist from knownCanonical genes (not exons) */
// I was not expecting it, but 20% of the KC genes overlap with others.
// Some are cDNAs, some are non-coding RNAs, some just look like junk.
// But I have to add special logic to accomodate them.
// Currently I just merge until there is no more overlap,
// and then I start a new region. So 30K regions should reduce to about 24K regions or less.
{
struct sqlConnection *conn = hAllocConn(database);
virtRegionList = NULL;
struct sqlResult *sr;
char **row;
char query[256];
sqlSafef(query, sizeof(query), "select chrom, chromStart, chromEnd from %s where chrom not like '%%_hap_%%' and chrom not like '%%_random'", table);
sr = sqlGetResult(conn, query);
char chrom[256] = "";
int start = -1;
int end = -1;
char lastChrom[256] = "";
int lastStart = -1;
int lastEnd = -1;
boolean firstTime = TRUE;
boolean isEOF = FALSE;
while (1)
{
boolean printIt = FALSE;
row = sqlNextRow(sr);
if (row)
{
safecpy(chrom, sizeof chrom, row[0]);
start = sqlUnsigned(row[1]);
end = sqlUnsigned(row[2]);
if (sameString(chrom, lastChrom))
{
if (start <= lastEnd)
{
// overlap detected in knownCanonical
// extend current region
if (end > lastEnd)
lastEnd = end;
}
else
{
printIt = TRUE;
}
}
else
{
printIt = TRUE;
}
}
else
{
printIt = TRUE;
isEOF = TRUE;
}
if (printIt)
{
if (firstTime)
{
firstTime = FALSE;
}
else
{
struct virtRegion *v;
AllocVar(v);
v->chrom = cloneString(lastChrom);
v->start = lastStart;
v->end = lastEnd;
v->strand[0] = '.'; // TODO we should probably just remove the strand field
v->strand[1] = 0;
slAddHead(&virtRegionList, v);
}
}
if (isEOF)
break;
if (printIt)
{
safecpy(lastChrom, sizeof lastChrom, chrom);
lastStart = start;
lastEnd = end;
}
}
sqlFreeResult(&sr);
slReverse(&virtRegionList);
hFreeConn(&conn);
}
struct kce
// keep list of overlapping genes and their exons
{
struct kce *next;
struct genePred *gene;
int exonNumber;
};
int findBestKce(struct kce *list, struct kce **pBestKce, struct kce **pPrevKce)
// find best kce by having minimum exon start
// TODO could replace this with a heap or a doubly-linked list
{
int best = -1;
struct kce *e, *prev = NULL;
for(e=list; e; prev=e, e=e->next)
{
int start = e->gene->exonStarts[e->exonNumber];
if ((start < best) || (best == -1))
{
best = start;
*pBestKce = e;
*pPrevKce = prev;
}
}
return best;
}
static void padExons(struct genePred *gene, int chromSize, int padding)
/* pad all of the exons */
{
int i;
for(i=0; i < gene->exonCount; ++i)
{
gene->exonStarts[i] -= padding;
if (gene->exonStarts[i] < 0)
gene->exonStarts[i] = 0;
gene->exonEnds[i] += padding;
if (gene->exonEnds[i] > chromSize)
gene->exonEnds[i] = chromSize;
}
}
static void convertGenePredGeneToExon(struct genePred *gene)
/* convert gene into a gene with just one exon that spans the entire gene */
{
if (gene->exonCount < 1)
errAbort("unexpected input in convertGenePredGeneToExon(), gene->exonCount=%d < 1", gene->exonCount);
gene->exonEnds[0] = gene->exonEnds[gene->exonCount - 1];
gene->exonCount = 1;
}
void initVirtRegionsFromEMGeneTableExons(boolean showNoncoding, char *knownCanonical, char *knownToTag, boolean geneMostly)
/* Create a regionlist from knownGene exons. */
// Merge exon regions that overlap.
// DONE Jim indicated that he would prefer it to include all transcripts, not just knownCanonical.
// DONE Jim also suggested that we might want to handle padding right here in this step.
// After thinking about it, I do not think it would be very hard because we are merging already.
// Basically, just take the record from the db table row, add padding to start and end,
// and clip for chromosome size.
// TODO If we keep it at full genome level (instead of single chrom), then there is an apparent
// sorting issue because although they are sorted on disk, they are usually sorted by chrom alphabetically
// so that chr11 (not chr2) comes after chr1. Instead of trying to specify the sort order in the query,
// which is slow, or trying to read one chrom at a time in the sorted order which is also slow, we can instead
// just fetch them in their native order, and then create a duplicate array and copy the contents
// to it in memory, one chunk per chrom, which would be very fast, but temporarily require duplicate vchrom array mem.
// Not sure what to do about assemblies with many scaffolds.
//
// Adding support for extra options from Gencode hg38 so we can filter for
// comprehensive, splice-variants, non-coding subsets.
{
struct sqlConnection *conn = hAllocConn(database);
virtRegionList = NULL;
struct sqlResult *sr;
char **row;
int rowOffset = 0;
char query[256];
int padding = emPadding;
if (sameString(virtModeType, "geneMostly"))
padding = gmPadding;
// knownCanonical Hash
struct hash *kcHash = NULL;
if (knownCanonical) // filter out alt splicing variants
{
// load up hash of canonical transcriptIds
sqlSafef(query, sizeof(query), "select transcript from %s"
//" where chrom not like '%%_hap_%%' and chrom not like '%%_random'"
, knownCanonical);
if (virtualSingleChrom())
sqlSafefAppend(query, sizeof(query), " where chrom='%s'", chromName);
kcHash = newHash(10);
sr = sqlGetResult(conn, query);
while ((row = sqlNextRow(sr)) != NULL)
{
hashAdd(kcHash, row[0], NULL);
}
sqlFreeResult(&sr);
}
// knownToTag basic hash
struct hash *ktHash = NULL;
if (knownToTag) // filter out all but Basic
{
// load up hash of canonical transcriptIds
sqlSafef(query, sizeof(query), "select name from %s where value='basic'", knownToTag);
ktHash = newHash(10);
sr = sqlGetResult(conn, query);
while ((row = sqlNextRow(sr)) != NULL)
{
hashAdd(ktHash, row[0], NULL);
}
sqlFreeResult(&sr);
}
setEMGeneTrack();
if (!emGeneTable)
errAbort("Unexpected error, emGeneTable=NULL in initVirtRegionsFromEMGeneTableExons");
if (hIsBinned(database, emGeneTable)) // skip first bin column if any
++rowOffset;
sqlSafef(query, sizeof(query), "select * from %s", emGeneTable);
if (virtualSingleChrom())
sqlSafefAppend(query, sizeof(query), " where chrom='%s'", chromName);
// TODO GALT may have to change this to in-memory sorting?
// refGene is out of order because of genbank continuous loading
// also, using where chrom= causes it to use indexes which disturb order returned.
sqlSafefAppend(query, sizeof(query), " order by chrom, txStart");
sr = sqlGetResult(conn, query);
char chrom[256] = "";
int start = -1;
int end = -1;
char lastChrom[256] = "";
int lastStart = -1;
int lastEnd = -1;
int chromSize = -1;
char lastChromSizeChrom[256] = "";
boolean firstTime = TRUE;
boolean isEOF = FALSE;
struct kce *kceList = NULL, *bestKce, *prevKce;
struct genePred *gene = NULL;
while (1)
{
while(1) // get input if possible
{
boolean readIt = FALSE;
if (!gene)
readIt = TRUE;
if (isEOF)
readIt = FALSE;
if (readIt)
{
row = sqlNextRow(sr);
if (row)
{
gene = genePredLoad(row+rowOffset);
if (geneMostly)
convertGenePredGeneToExon(gene);
if (!sameString(lastChromSizeChrom, gene->chrom))
{
chromSize = hChromSize(database, gene->chrom);
safecpy(lastChromSizeChrom, sizeof lastChromSizeChrom, gene->chrom);
}
if (padding > 0)
padExons(gene, chromSize, padding); // handle padding
}
else
{
isEOF = TRUE;
}
}
if (gene && !showNoncoding && (gene->cdsStart == gene->cdsEnd))
{
//skip non-coding gene
genePredFree(&gene);
}
if (gene && knownCanonical && !hashLookup(kcHash, gene->name))
{
//skip gene not in knownCanonical hash
genePredFree(&gene);
}
if (gene && knownToTag && !hashLookup(ktHash, gene->name))
{
// skip gene not in knownToTag Basic hash
genePredFree(&gene);
}
boolean transferIt = FALSE;
if (gene && !kceList)
{
transferIt = TRUE;
}
else if (gene && kceList)
{
// TODO need to check the chrom equality first
int best = findBestKce(kceList, &bestKce, &prevKce);
if (sameString(gene->chrom, chrom))
{
if (gene->exonStarts[0] < best)
transferIt = TRUE;
}
}
if (transferIt)
{
// add gene to kce list
struct kce *kce;
AllocVar(kce);
kce->gene = gene;
kce->exonNumber = 0;
slAddHead(&kceList, kce);
safecpy(chrom, sizeof chrom, gene->chrom);
gene = NULL; // do not free since it is still in use
}
if (gene && kceList && !transferIt)
break;
if (isEOF && !gene)
{
if (kceList) // flush out the last of the items in kcelist
findBestKce(kceList, &bestKce, &prevKce);
break;
}
}
boolean printIt = FALSE;
if (kceList)
{
safecpy(chrom, sizeof chrom, bestKce->gene->chrom);
start = bestKce->gene->exonStarts[bestKce->exonNumber];
end = bestKce->gene->exonEnds[bestKce->exonNumber];
if (sameString(chrom, lastChrom))
{
if (start <= lastEnd)
{
// overlap detected, extend current region
if (end > lastEnd)
{
lastEnd = end;
}
}
else
{
printIt = TRUE;
}
}
else
{
printIt = TRUE;
}
}
else
{
printIt = TRUE;
isEOF = TRUE;
}
if (printIt)
{
if (firstTime)
{
firstTime = FALSE;
}
else
{
struct virtRegion *v;
AllocVar(v);
v->chrom = cloneString(lastChrom);
v->start = lastStart;
v->end = lastEnd;
v->strand[0] = '.'; // TODO we should probably just remove the strand field
v->strand[1] = 0;
slAddHead(&virtRegionList, v);
}
}
if (isEOF && !kceList && !gene)
break;
if (printIt)
{
safecpy(lastChrom, sizeof lastChrom, chrom);
lastStart = start;
lastEnd = end;
}
++bestKce->exonNumber;
if (bestKce->exonNumber >= bestKce->gene->exonCount)
{ // remove from kceList
genePredFree(&bestKce->gene);
if (prevKce)
prevKce->next = bestKce->next;
else
kceList = bestKce->next;
freeMem(bestKce);
}
}
sqlFreeResult(&sr);
slReverse(&virtRegionList);
hashFree(&kcHash);
hashFree(&ktHash);
hFreeConn(&conn);
}
void testRegionList()
/* check if it is ascending non-overlapping regions.
(this is not always a requirement in the most general case, i.e. user-regions)
*/
{
char lastChrom[256];
int lastEnd = -1;
struct virtRegion *v;
for (v=virtRegionList; v; v=v->next)
{
if (sameString(v->chrom,lastChrom))
{
if (v->end < v->start)
errAbort("check of region list reveals invalid region %s:%d-%d", v->chrom, v->start, v->end);
if (lastEnd > v->start)
errAbort("check of region list reveals overlapping regions region %s:%d-%d lastEnd=%d", v->chrom, v->start, v->end, lastEnd);
}
else
{
safecpy(lastChrom, sizeof lastChrom, v->chrom);
}
lastEnd = v->end;
}
}
// multi-window variables global to hgTracks
void setGlobalsFromWindow(struct window *window)
/* set global window values */
{
currentWindow = window;
organism = window->organism;
database = window->database;
chromName = window->chromName;
winStart = window->winStart;
winEnd = window->winEnd;
insideX = window->insideX;
insideWidth = window->insideWidth;
winBaseCount = winEnd - winStart;
}
void initExonStep()
/* create exon-like pattern with exonSize and stepSize */
{
int winCount = cartUsualInt(cart, "demo2NumWindows", demo2NumWindows);
int i;
int exonSize = cartUsualInt(cart, "demo2WindowSize", demo2WindowSize); //200; //9974; //200;
int intronSize = cartUsualInt(cart, "demo2StepSize", demo2StepSize); //200; //15000; // really using it like stepSize as that allows overlapping windows.
struct virtRegion *v;
for(i=0;ichrom = "chr21";
v->start = 33031597 - 1 + i*(intronSize);
v->end = v->start + exonSize; //33041570;
slAddHead(&virtRegionList, v);
}
slReverse(&virtRegionList);
//if (winCount >= 2)
// withNextExonArrows = FALSE; /* Display next exon navigation buttons near center labels? */
//warn("winCount=%d, exonSize=%d, intronSize=%d", winCount, exonSize, intronSize);
}
void initAllChroms()
/* initialize virt region list for main chromosomes */
{
struct sqlConnection *conn = hAllocConn(database);
struct sqlResult *sr;
char **row;
int winCount = 0;
char *query =
NOSQLINJ "select chrom, size from chromInfo"
" where chrom like 'chr%'"
" and chrom not like '%_random'"
" and chrom not like 'chrUn%'";
// allow alternate haplotypes for now
//" and chrom not like '%_hap%'"
//" and chrom not like '%_alt'"
//warn("%s",query);
struct virtRegion *v;
sr = sqlGetResult(conn, query);
while ((row = sqlNextRow(sr)) != NULL)
{
unsigned chromSize = sqlUnsigned(row[1]);
AllocVar(v);
v->chrom = cloneString(row[0]);
v->start = 1 - 1;
v->end = chromSize;
slAddHead(&virtRegionList, v);
++winCount;
}
sqlFreeResult(&sr);
hFreeConn(&conn);
slReverse(&virtRegionList);
}
void initWindowsAltLoci()
/* initialize window list showing alt (alternate haplotype)*/
{
struct virtRegion *v;
//chr1:153520530-153700530
AllocVar(v);
v->chrom = "chr1";
v->start = 153520530 - 1;
v->end = 153700530;
//chr1:153,520,529-154,045,739 as a single window
slAddHead(&virtRegionList, v);
//chr1_GL383518v1_alt:1-182439
AllocVar(v);
v->chrom = "chr1_GL383518v1_alt";
v->start = 1 - 1;
v->end = 182439;
slAddHead(&virtRegionList, v);
//chr1:153865739-154045739
AllocVar(v);
v->chrom = "chr1";
v->start = 153865739 - 1;
v->end = 154045739;
slAddHead(&virtRegionList, v);
slReverse(&virtRegionList);
}
void checkmultiRegionsBedInput()
/* Check if multiRegionsBedInput needs processing.
* If BED submitted, see if it has changed, and if so, save it to trash
* and update cart and global vars. Uses sha1 hash for faster change check. */
{
enum custRgnType { empty, url, trashFile };
enum custRgnType oldType = empty;
enum custRgnType newType = empty;
// OLD input
char *newMultiRegionsBedUrl = NULL;
multiRegionsBedUrl = cartUsualString(cart, "multiRegionsBedUrl", multiRegionsBedUrl);
char multiRegionsBedUrlSha1Name[1024];
safef(multiRegionsBedUrlSha1Name, sizeof multiRegionsBedUrlSha1Name, "%s.sha1", multiRegionsBedUrl);
if (!multiRegionsBedUrl)
{
multiRegionsBedUrl = "";
cartSetString(cart, "multiRegionsBedUrl", multiRegionsBedUrl);
}
if (sameString(multiRegionsBedUrl,""))
oldType = empty;
else if (strstr(multiRegionsBedUrl,"://"))
oldType = url;
else
oldType = trashFile;
// NEW input
char *multiRegionsBedInput = cartOptionalString(cart, "multiRegionsBedInput");
if (!multiRegionsBedInput)
return;
// create cleaned up dyString from input.
// remove blank lines, trim leading and trailing spaces, change CRLF from TEXTAREA input to LF.
struct dyString *dyInput = dyStringNew(1024);
char *input = cloneString(multiRegionsBedInput); // make a copy, linefile modifies
struct lineFile *lf = lineFileOnString("multiRegionsBedInput", TRUE, input);
char *line;
int lineSize;
while (lineFileNext(lf, &line, &lineSize))
{
line = trimSpaces(line);
if (sameString(line, "")) // skip blank lines
continue;
dyStringAppend(dyInput,line);
dyStringAppend(dyInput,"\n");
}
lineFileClose(&lf);
// test multiRegionsBedInput. empty? url? trashFile?
input = cloneString(dyInput->string); // make a copy, linefile modifies
lf = lineFileOnString("multiRegionsBedInput", TRUE, input);
int lineCount = 0;
while (lineFileNext(lf, &line, &lineSize))
{
++lineCount;
if (lineCount==1 &&
(startsWithNoCase("http://" ,line)
|| startsWithNoCase("https://",line)
|| startsWithNoCase("ftp://" ,line)))
{
// new value is a URL. set vars and cart.
newMultiRegionsBedUrl = cloneString(line);
newType = url;
}
break;
}
lineFileClose(&lf);
if (newType != url)
{
if (lineCount == 0) // there are no non-blank lines
{
newMultiRegionsBedUrl = "";
newType = empty;
}
else
newType = trashFile;
}
char newSha1[(SHA_DIGEST_LENGTH + 1) * 2];
if (newType==trashFile)
{
// calculate sha1 checksum on new input.
unsigned char hash[SHA_DIGEST_LENGTH];
SHA1((const unsigned char *)dyInput->string, dyInput->stringSize, hash);
hexBinaryString(hash, SHA_DIGEST_LENGTH, newSha1, (SHA_DIGEST_LENGTH + 1) * 2);
}
// compare input sha1 to trashFile sha1 to see if same
boolean filesAreSame = FALSE;
if (oldType==trashFile && newType==trashFile)
{
lf = lineFileMayOpen(multiRegionsBedUrlSha1Name, TRUE);
while (lineFileNext(lf, &line, &lineSize))
{
if (sameString(line, newSha1))
filesAreSame = TRUE;
}
lineFileClose(&lf);
}
// save new trashFile unless no changes.
if (newType==trashFile && (!(oldType==trashFile && filesAreSame) ))
{
struct tempName bedTn;
trashDirFile(&bedTn, "hgt", "custRgn", ".bed");
FILE *f = mustOpen(bedTn.forCgi, "w");
mustWrite(f, dyInput->string, dyInput->stringSize);
carefulClose(&f);
// new value is a trash file.
newMultiRegionsBedUrl = cloneString(bedTn.forCgi);
// save new input sha1 to trash file.
safef(multiRegionsBedUrlSha1Name, sizeof multiRegionsBedUrlSha1Name, "%s.sha1", bedTn.forCgi);
f = mustOpen(multiRegionsBedUrlSha1Name, "w");
mustWrite(f, newSha1, strlen(newSha1));
carefulClose(&f);
}
dyStringFree(&dyInput);
// if new value, set vars and cart
if (newMultiRegionsBedUrl)
{
multiRegionsBedUrl = newMultiRegionsBedUrl;
cartSetString(cart, "multiRegionsBedUrl", multiRegionsBedUrl);
}
cartRemove(cart, "multiRegionsBedInput");
}
boolean initVirtRegionsFromBedUrl(time_t *bedDateTime)
/* Read custom regions from BED URL */
{
multiRegionsBedUrl = cartUsualString(cart, "multiRegionsBedUrl", multiRegionsBedUrl);
int bedPadding = 0; // default no padding
if (sameString(multiRegionsBedUrl,""))
{
warn("No BED or BED URL specified.");
return FALSE;
}
struct lineFile *lf = NULL;
if (strstr(multiRegionsBedUrl,"://"))
{
lf = lineFileUdcMayOpen(multiRegionsBedUrl, FALSE);
if (!lf)
{
warn("Unable to open [%s] with udc", multiRegionsBedUrl);
return FALSE;
}
*bedDateTime = udcTimeFromCache(multiRegionsBedUrl, NULL);
}
else
{
lf = lineFileMayOpen(multiRegionsBedUrl, TRUE);
if (!lf)
{
warn("BED custom regions file [%s] not found.", multiRegionsBedUrl);
return FALSE;
}
*bedDateTime = 0;
// touch corresponding .sha1 file to save it from trash cleaner.
char multiRegionsBedUrlSha1Name[1024];
safef(multiRegionsBedUrlSha1Name, sizeof multiRegionsBedUrlSha1Name, "%s.sha1", multiRegionsBedUrl);
if (fileExists(multiRegionsBedUrlSha1Name))
readAndIgnore(multiRegionsBedUrlSha1Name);
}
char *line;
int lineSize;
int expectedFieldCount = -1;
struct bed *bed, *bedList = NULL;
while (lineFileNext(lf, &line, &lineSize))
{
// Process comments for keywords like database, shortDesc, and maybe others
if (startsWith("#",line))
{
if (startsWith("#database ",line))
{
char *dbFromBed = line+strlen("#database ");
if (!sameString(database,dbFromBed))
{
warn("Multi-Region BED URL error: The database (%s) specified in input does not match current database %s",
dbFromBed, database);
return FALSE;
}
}
if (startsWith("#shortDesc ",line))
{
virtModeShortDescr = cloneString(line+strlen("#shortDesc "));
}
if (startsWith("#padding ",line))
{
bedPadding = sqlSigned(line+strlen("#padding "));
}
continue;
}
char *row[15];
int numFields = chopByWhite(line, row, ArraySize(row));
if (numFields < 3)
{
warn("%s doesn't appear to be in BED format. 3 or more fields required, got %d",
multiRegionsBedUrl, numFields);
return FALSE;
}
if (expectedFieldCount == -1)
{
expectedFieldCount = numFields;
}
else
{
if (numFields != expectedFieldCount)
errAbort("Multi-Region BED was detected to have %d columns. But this row has %d columns. "
"All rows except comment lines should have the same number of columns", numFields, expectedFieldCount);
}
AllocVar(bed);
// All fields are standard BED fields, no bedplus fields supported at this time.
// note: this function does not validate chrom name or end beyond chrom size
struct errCatch *errCatch = errCatchNew();
if (errCatchStart(errCatch))
{
loadAndValidateBed(row, numFields, numFields+0, lf, bed, NULL, TRUE); // can errAbort
}
errCatchEnd(errCatch);
if (errCatch->gotError)
{
warn("%s", errCatch->message->string);
return FALSE;
}
errCatchFree(&errCatch);
bed->chrom=cloneString(bed->chrom); // loadAndValidateBed does not do it for speed. but bedFree needs it.
struct chromInfo *ci = hGetChromInfo(database, bed->chrom);
if (ci == NULL)
{
warn("Couldn't find chromosome/scaffold %s in database", bed->chrom);
return FALSE;
}
if (bed->chromEnd > ci->size)
{
warn("BED chromEnd %u > size %u for chromosome/scaffold %s", bed->chromEnd, ci->size, bed->chrom);
return FALSE;
}
slAddHead(&bedList, bed);
struct virtRegion *v;
if (numFields < 12)
{
AllocVar(v);
v->chrom = cloneString(bed->chrom);
v->start = bed->chromStart;
v->end = bed->chromEnd;
slAddHead(&virtRegionList, v);
}
else
{
int e;
for (e = 0; e < bed->blockCount; ++e)
{
AllocVar(v);
v->chrom = cloneString(bed->chrom);
v->start = bed->chromStart + bed->chromStarts[e];
v->end = v->start + bed->blockSizes[e];
slAddHead(&virtRegionList, v);
}
}
}
lineFileClose(&lf);
bedFreeList(&bedList);
slReverse(&virtRegionList);
if (bedPadding > 0)
padVirtRegions(bedPadding);
return TRUE;
}
void restoreSavedVirtPosition()
/* Set state from lastDbPosCart.
* This involves parsing the extra state that was saved.*/
{
struct hashEl *el, *elList = hashElListHash(lastDbPosCart->hash);
for (el = elList; el != NULL; el = el->next)
{
char *cartVar = el->name;
char *cartVal = cartOptionalString(lastDbPosCart, cartVar);
if (cartVal)
{
/* do we need this feature?
if (sameString(cartVal,"(null)"))
cartRemove(cart, cartVar);
else
*/
cartSetString(cart, cartVar, cartVal);
}
}
hashElFreeList(&elList);
}
void lastDbPosSaveCartSetting(char *cartVar)
/* Save var and value from cart into lastDbPosCart. */
{
cartSetString(lastDbPosCart, cartVar, cartUsualString(cart, cartVar, NULL));
}
void dySaveCartSetting(struct dyString *dy, char *cartVar, boolean saveBoth)
/* Grab var and value from cart, save as var=val to dy string. */
{
if (dy->stringSize > 0)
dyStringAppend(dy, " ");
dyStringPrintf(dy, "%s=%s", cartVar, cartUsualString(cart, cartVar, NULL));
if (saveBoth)
lastDbPosSaveCartSetting(cartVar);
}
boolean initRegionList()
/* initialize window list */
{
checkmultiRegionsBedInput();
struct virtRegion *v;
virtRegionList = NULL;
virtModeExtraState = ""; // This is state that determines if the virtChrom has changed
lastDbPosCart = cartOfNothing(); // USED to store and restore cart settings related to position and virtMode
struct dyString *dy = dyStringNew(256); // used to build virtModeExtraState
if (sameString(virtModeType, "default"))
{
// Single window same as normal window
// mostly good to test nothing was broken with single window
AllocVar(v);
v->chrom = chromName;
v->start = 0;
v->end = hChromSize(database, chromName);
virtWinStart = winStart;
virtWinEnd = winEnd;
slAddHead(&virtRegionList, v);
slReverse(&virtRegionList);
}
else if (sameString(virtModeType, "exonMostly")
|| sameString(virtModeType, "geneMostly"))
{
// Gencode settings: comprehensive, alt-splice, non-coding
char *knownCanonical = NULL; // show splice-variants, not filtered out via knownCanonical
boolean showNoncoding = TRUE; // show non-coding where cdsStart==cdsEnd
char *knownToTag = NULL; // show comprehensive set not filtered by knownToTag
char varName[SMALLBUF];
boolean geneMostly = FALSE;
lastDbPosSaveCartSetting("emGeneTable");
//DISGUISE makes obsolete dySaveCartSetting(dy, "emGeneTable");
//DISGUISE makes obsolete dySaveCartSetting(dy, "emPadding");
if (sameString(virtModeType, "geneMostly"))
geneMostly = TRUE;
if (sameString(emGeneTable, "knownGene"))
{
// test cart var knownGene.show.noncoding
// check for alternate table name.
// if found, set and pass to gene-table reading routine
// Some code borrowed from simpleTracks.c::loadKnownGene()
safef(varName, sizeof(varName), "%s.show.noncoding", emGeneTable);
showNoncoding = cartUsualBoolean(cart, varName, TRUE);
//DISGUISE makes obsolete dySaveCartSetting(dy, varName);
safef(varName, sizeof(varName), "%s.show.spliceVariants", emGeneTable);
boolean showSpliceVariants = cartUsualBoolean(cart, varName, TRUE);
//DISGUISE makes obsolete dySaveCartSetting(dy, varName);
if (!showSpliceVariants)
{
char *canonicalTable = trackDbSettingOrDefault(emGeneTrack->tdb, "canonicalTable", "knownCanonical");
if (hTableExists(database, canonicalTable))
knownCanonical = canonicalTable;
}
safef(varName, sizeof(varName), "%s.show.comprehensive", emGeneTable);
boolean showComprehensive = cartUsualBoolean(cart, varName, FALSE);
//DISGUISE makes obsolete dySaveCartSetting(dy, varName);
if (!showComprehensive)
{
if (hTableExists(database, "knownToTag"))
{
knownToTag = "knownToTag";
}
}
}
if (sameString(emGeneTable, "refGene"))
{
char varName[SMALLBUF];
safef(varName, sizeof(varName), "%s.hideNoncoding", emGeneTable);
showNoncoding = !cartUsualBoolean(cart, varName, FALSE);
//DISGUISE makes obsolete dySaveCartSetting(dy, varName);
}
initVirtRegionsFromEMGeneTableExons(showNoncoding, knownCanonical, knownToTag, geneMostly);
if (!virtRegionList)
{
warn("No genes found on chrom %s for track %s, returning to default view", chromName, emGeneTrack->shortLabel);
return FALSE; // regionList is empty, nothing found.
}
if (geneMostly)
virtModeShortDescr = "genes";
else
virtModeShortDescr = "exons";
// DISGUISE makes obsolete dyStringPrintf(dy," %s %s", dy->string, knownCanonical, knownToTag);
}
else if (sameString(virtModeType, "kcGenes")) // TODO obsolete
{
initVirtRegionsFromKnownCanonicalGenes("knownCanonical");
virtModeShortDescr = "genes";
}
else if (sameString(virtModeType, "customUrl"))
{
// custom regions from BED URL
virtModeShortDescr = "customUrl"; // can be overridden by comment in input bed file
time_t bedDateTime = 0;
if (!initVirtRegionsFromBedUrl(&bedDateTime))
{
return FALSE; // return to default mode
}
dySaveCartSetting(dy, "multiRegionsBedUrl", TRUE);
dyStringPrintf(dy, " %ld", (long)bedDateTime);
}
else if (sameString(virtModeType, "singleTrans"))
{
singleTransId = cartUsualString(cart, "singleTransId", singleTransId);
if (sameString(singleTransId, ""))
{
warn("Single transcript Id should not be blank");
return FALSE; // return to default mode
}
setEMGeneTrack();
dySaveCartSetting(dy, "singleTransId", TRUE);
}
else if (sameString(virtModeType, "singleAltHaplo"))
{
singleAltHaploId = cartUsualString(cart, "singleAltHaploId", singleAltHaploId); // default is chr6_cox_hap2
initAllChroms(); // we want to default to full genome view.
if (!initSingleAltHaplotype(singleAltHaploId))
{
virtRegionList = NULL;
return FALSE; // return to default mode
}
// was "single haplo" but that might confuse some users.
virtModeShortDescr = endsWith(singleAltHaploId, "_fix") ? "fix patch" : "alt haplo";
dySaveCartSetting(dy, "singleAltHaploId", TRUE);
}
else if (sameString(virtModeType, "allChroms"))
{ // TODO more work on this mode
//warn("show all regular chromosomes (not alts)\n"
//"Warning must turn off all tracks except big*");
initAllChroms();
}
else if (sameString(virtModeType, "demo1"))
{
// demo two windows on two chroms (default posn chr21, and same loc on chr22
//chr21:33,031,597-33,041,570
AllocVar(v);
//chr21:33,031,597-33,041,570
v->chrom = "chr21";
v->start = 33031597 - 1;
v->end = 33041570;
slAddHead(&virtRegionList, v);
struct virtRegion *v2;
AllocVar(v2);
//chr22:33,031,597-33,041,570
v2->chrom = "chr22";
v2->start = 33031597 - 1;
v2->end = 33041570;
slAddHead(&virtRegionList, v2);
slReverse(&virtRegionList);
}
else if (sameString(virtModeType, "demo2"))
{
// demo multiple (70) windows on one chrom chr21 def posn, window size and step exon-like
initExonStep();
}
else if (sameString(virtModeType, "demo4"))
{
// demo multiple (20) windows showing exons from TITIN gene uc031rqd.1.
initVirtRegionsFromEmGeneTable("uc031rqd.1"); // TITIN // "uc002ypa.3"); // SOD1
}
else if (sameString(virtModeType, "demo5"))
{
// demo alt locus on hg38
// Shows alt chrom surrounded by preceding and following regions of same size from reference genome.
initWindowsAltLoci();
}
else if (sameString(virtModeType, "demo6"))
{
// demo SOD1
// Shows zoomed in exon-exon junction from SOD1 gene, between exon1 and exon2.
initVirtRegionsFromSOD1Hardwired();
}
else
{
// Unrecognized virtModeType
return FALSE; // return to default mode
}
virtModeExtraState = dyStringCannibalize(&dy);
if (!virtRegionList)
return FALSE; // regionList is empty, nothing found.
return TRUE;
}
boolean isLimitedVisHiddenForAllWindows(struct track *track)
/* Check if track limitedVis == hidden for all windows.
* Return true if all are hidden */
{
boolean result = TRUE;
for(;track;track=track->nextWindow)
if (track->limitedVis != tvHide)
result = FALSE;
return result;
}
boolean isTypeBedLike(struct track *track)
/* Check if track type is BED-like packable thing (but not rmsk or joinedRmsk) */
{ // TODO GALT do we have all the types needed?
// TODO could it be as simple as whether track->items exists?
char *typeLine = track->tdb->type, *words[8], *type;
int wordCount;
if (typeLine == NULL)
return FALSE;
wordCount = chopLine(cloneString(typeLine), words);
if (wordCount <= 0)
return FALSE;
type = words[0];
// NOTE: if type is missing here, full mode fails to return an hgTracks object
if (
( sameWord(type, "bed")
|| sameWord(type, "bed5FloatScore")
|| sameWord(type, "bed6FloatScore")
|| sameWord(type, "bedDetail")
|| sameWord(type, "bigBed")
|| sameWord(type, "bigGenePred")
|| sameWord(type, "broadPeak")
|| sameWord(type, "chain")
|| sameWord(type, "factorSource")
|| sameWord(type, "genePred")
|| sameWord(type, "gvf")
|| sameWord(type, "narrowPeak")
|| sameWord(type, "psl")
|| sameWord(type, "barChart")
|| sameWord(type, "bigBarChart")
|| sameWord(type, "interact")
|| sameWord(type, "bigInteract")
|| sameWord(type, "bigLolly")
//|| track->loadItems == loadSimpleBed
//|| track->bedSize >= 3 // should pick up several ENCODE BED-Plus types.
)
&& track->canPack
)
{
return TRUE;
}
return FALSE;
}
boolean isTypeUseItemNameAsKey(struct track *track)
/* Check if track type is like expRatio and key is just item name. */
{
char *typeLine = track->tdb->type, *words[8], *type;
int wordCount;
if (typeLine == NULL)
return FALSE;
wordCount = chopLine(cloneString(typeLine), words);
if (wordCount <= 0)
return FALSE;
type = words[0];
if (sameWord(type, "expRatio"))
{
// track is like expRatio, needs one row per item
return TRUE;
}
return FALSE;
}
void setFlatTrackMaxHeight(struct flatTracks *flatTrack, int fontHeight)
/* for each flatTrack, figure out maximum height needed from all windows */
{
struct track *track = flatTrack->track;
int maxHeight = 0;
struct track *winTrack;
struct window *window;
for (window=windows, winTrack=track; window; window=window->next, winTrack=winTrack->nextWindow)
{
setGlobalsFromWindow(window);
int trackHeight = trackPlusLabelHeight(winTrack, fontHeight);
if (trackHeight > maxHeight)
maxHeight = trackHeight;
}
setGlobalsFromWindow(windows); // first window
flatTrack->maxHeight = maxHeight;
}
boolean doCollapseEmptySubtracks(struct track *track)
/* Suppress display of empty subtracks. Initial support only for bed's. */
{
char *collapseEmptySubtracks = trackDbSetting(track->tdb, "collapseEmptySubtracks");
return (collapseEmptySubtracks && sameWord(collapseEmptySubtracks, "on"));
}
void makeActiveImage(struct track *trackList, char *psOutput)
/* Make image and image map. */
{
struct track *track;
MgFont *font = tl.font;
struct hvGfx *hvg;
struct tempName pngTn;
char *mapName = "map";
int fontHeight = mgFontLineHeight(font);
int trackPastTabX = (withLeftLabels ? trackTabWidth : 0);
int trackTabX = gfxBorder;
int trackPastTabWidth = tl.picWidth - trackPastTabX;
int pixWidth, pixHeight;
int y=0;
int titleHeight = fontHeight;
int scaleBarPad = 2;
int scaleBarHeight = fontHeight;
int scaleBarTotalHeight = fontHeight + 2 * scaleBarPad;
int showPosHeight = fontHeight;
int rulerHeight = fontHeight;
int baseHeight = fontHeight;
int basePositionHeight = rulerHeight;
int codonHeight = fontHeight;
int rulerTranslationHeight = codonHeight * 3; // 3 frames
int yAfterRuler = gfxBorder;
int yAfterBases = yAfterRuler; // differs if base-level translation shown
boolean rulerCds = zoomedToCdsColorLevel;
int rulerClickHeight = 0;
int newWinWidth = 0;
/* Figure out dimensions and allocate drawing space. */
pixWidth = tl.picWidth;
leftLabelX = gfxBorder;
leftLabelWidth = insideX - gfxBorder*3;
struct image *theOneImg = NULL; // No need to be global, only the map needs to be global
struct image *theSideImg = NULL; // Because dragScroll drags off end of image,
// the side label gets seen. Therefore we need 2 images!!
//struct imgTrack *curImgTrack = NULL; // Make this global for now to avoid huge rewrite
struct imgSlice *curSlice = NULL; // No need to be global, only the map needs to be global
//struct mapSet *curMap = NULL; // Make this global for now to avoid huge rewrite
// Set up imgBox dimensions
int sliceWidth[stMaxSliceTypes]; // Just being explicit
int sliceOffsetX[stMaxSliceTypes];
int sliceHeight = 0;
int sliceOffsetY = 0;
char *rulerTtl = NULL;
if (theImgBox)
// if theImgBox == NULL then we are rendering a simple single image such as right-click View image.
// theImgBox is a global for now to avoid huge rewrite of hgTracks. It is started
// prior to this in doTrackForm()
{
rulerTtl = "drag select or click to zoom";
hPrintf("\n", database);
hPrintf("\n", chromName);
hPrintf("\n", winStart);
hPrintf("\n", winEnd);
hPrintf("\n", tl.picWidth);
// If a portal was established, then set the global dimensions to the entire expanded image size
if (imgBoxPortalDimensions(theImgBox,&virtWinStart,&virtWinEnd,&(tl.picWidth),NULL,NULL,NULL,NULL,NULL))
{
pixWidth = tl.picWidth;
virtWinBaseCount = virtWinEnd - virtWinStart;
fullInsideWidth = tl.picWidth - gfxBorder - fullInsideX;
}
memset((char *)sliceWidth, 0,sizeof(sliceWidth));
memset((char *)sliceOffsetX,0,sizeof(sliceOffsetX));
if (withLeftLabels)
{
sliceWidth[stButton] = trackTabWidth + 1;
sliceWidth[stSide] = leftLabelWidth - sliceWidth[stButton] + 1;
sliceOffsetX[stSide] =
(revCmplDisp ? (tl.picWidth - sliceWidth[stSide] - sliceWidth[stButton])
: sliceWidth[stButton]);
sliceOffsetX[stButton] = (revCmplDisp ? (tl.picWidth - sliceWidth[stButton]) : 0);
}
sliceOffsetX[stData] = (revCmplDisp ? 0 : sliceWidth[stSide] + sliceWidth[stButton]);
sliceWidth[stData] = tl.picWidth - (sliceWidth[stSide] + sliceWidth[stButton]);
}
struct flatTracks *flatTracks = NULL;
struct flatTracks *flatTrack = NULL;
if (rulerMode != tvFull)
{
rulerCds = FALSE;
}
/* Figure out height of each visible track. */
pixHeight = gfxBorder;
// figure out height of ruler
if (rulerMode != tvHide)
{
if (!baseShowRuler && !baseTitle && !baseShowPos && !baseShowAsm && !baseShowScaleBar && !zoomedToBaseLevel && !rulerCds)
{
warn("Can't turn everything off in base position track. Turning ruler back on");
baseShowRuler = TRUE;
cartSetBoolean(cart, BASE_SHOWRULER, TRUE);
}
if (baseTitle)
basePositionHeight += titleHeight;
if (baseShowPos||baseShowAsm)
basePositionHeight += showPosHeight;
if (baseShowScaleBar)
basePositionHeight += scaleBarTotalHeight;
if (!baseShowRuler)
{
basePositionHeight -= rulerHeight;
rulerHeight = 0;
}
if (zoomedToBaseLevel)
basePositionHeight += baseHeight;
yAfterRuler += basePositionHeight;
yAfterBases = yAfterRuler;
pixHeight += basePositionHeight;
if (rulerCds)
{
yAfterRuler += rulerTranslationHeight;
pixHeight += rulerTranslationHeight;
}
}
/* Hash tracks/subtracks, limit visibility and calculate total image height: */
// For multiple windows, sets height and visibility
// as well as making the flatTrack list.
// TODO there is a chance that for pack and squish
// I might need to trigger a track-height check here
// since it is after all items are loaded for all windows
// but before things are checked for overflow or limitedVis?
// The fixed non-overflow tracks like knownGene used to initialize
// ss and track height during loadItems(). That was delayed
// because we now need all windows to be fully loaded before
// calculating their joint ss layout and height.
// set heights and visibilities.
struct window *window = NULL;
for(window=windows;window;window=window->next)
{
setGlobalsFromWindow(window);
trackList = window->trackList;
for (track = trackList; track != NULL; track = track->next)
{
if (tdbIsCompositeChild(track->tdb)) // When single track is requested via AJAX,
{
limitedVisFromComposite(track); // it could be a subtrack
}
else
{
limitVisibility(track);
}
if (tdbIsComposite(track->tdb))
{
struct track *subtrack;
for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next)
{
if (!isSubtrackVisible(subtrack))
continue;
// subtrack vis can be explicit or inherited from composite/view.
// Then it could be limited because of pixel height
limitedVisFromComposite(subtrack);
if (subtrack->limitedVis != tvHide)
{
subtrack->hasUi = track->hasUi;
}
}
}
}
}
trackList = windows->trackList;
setGlobalsFromWindow(windows); // first window
struct slName *orderedWiggles = NULL;
char *sortTrack;
char *wigOrder = NULL;
if ((sortTrack = cgiOptionalString( "sortSim")) != NULL)
{
char buffer[1024];
safef(buffer, sizeof buffer, "simOrder_%s", sortTrack);
wigOrder = cartString(cart, buffer);
}
if ((sortTrack = cgiOptionalString( "sortExp")) != NULL)
{
char buffer[1024];
safef(buffer, sizeof buffer, "expOrder_%s", sortTrack);
wigOrder = cartString(cart, buffer);
}
if (wigOrder != NULL)
{
orderedWiggles = slNameListFromString(wigOrder, ' ');
struct slName *name = orderedWiggles;
// if we're sorting, remove existing sort order for this composite
for(; name; name = name->next)
{
char buffer[1024];
safef(buffer, sizeof buffer, "%s_imgOrd", name->name);
cartRemove(cart, buffer);
}
}
// Construct flatTracks
for (track = trackList; track != NULL; track = track->next)
{
if (tdbIsComposite(track->tdb))
{
struct track *subtrack;
if (isCompositeInAggregate(track))
flatTracksAdd(&flatTracks,track,cart, orderedWiggles);
else
{
boolean doCollapse = doCollapseEmptySubtracks(track);
for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next)
{
if (!isSubtrackVisible(subtrack))
continue;
if (!isLimitedVisHiddenForAllWindows(subtrack) &&
!(doCollapse && slCount(subtrack->items) == 0))
{
flatTracksAdd(&flatTracks,subtrack,cart, orderedWiggles);
}
}
}
}
else
{
if (!isLimitedVisHiddenForAllWindows(track))
{
flatTracksAdd(&flatTracks,track,cart, orderedWiggles);
}
}
}
flatTracksSort(&flatTracks); // Now we should have a perfectly good flat track list!
if (orderedWiggles)
{
// save order to cart
struct flatTracks *ft;
char buffer[4096];
int count = 1;
for(ft = flatTracks; ft; ft = ft->next)
{
safef(buffer, sizeof buffer, "%s_imgOrd", ft->track->track);
cartSetInt(cart, buffer, count++);
}
}
// for each track, figure out maximum height needed from all windows
for (flatTrack = flatTracks; flatTrack != NULL; flatTrack = flatTrack->next)
{
track = flatTrack->track;
if (track->limitedVis == tvHide)
{
continue;
}
setFlatTrackMaxHeight(flatTrack, fontHeight);
}
// fill out track->prevTrack, and check for maxSafeHeight
boolean safeHeight = TRUE;
/* firefox on Linux worked almost up to 34,000 at the default 620 width */
#define maxSafeHeight 32000
struct track *prevTrack = NULL;
for (flatTrack = flatTracks,prevTrack=NULL; flatTrack != NULL; flatTrack = flatTrack->next)
{
track = flatTrack->track;
assert(track->limitedVis != tvHide);
// ORIG int totalHeight = pixHeight+trackPlusLabelHeight(track,fontHeight);
int totalHeight = pixHeight+flatTrack->maxHeight;
if (totalHeight > maxSafeHeight)
{
char numBuf[SMALLBUF];
sprintLongWithCommas(numBuf, maxSafeHeight);
if (safeHeight) // Only one message
warn("Image is over %s pixels high (%d pix) at the following track which is now "
"hidden: \"%s\".%s", numBuf, totalHeight, track->tdb->longLabel,
(flatTrack->next != NULL ?
"\nAdditional tracks may have also been hidden at this zoom level." : ""));
safeHeight = FALSE;
struct track *winTrack;
for(winTrack=track;winTrack;winTrack=winTrack->nextWindow)
{
track->limitedVis = tvHide;
track->limitedVisSet = TRUE;
}
}
if (!isLimitedVisHiddenForAllWindows(track))
{
struct track *winTrack;
for(winTrack=track;winTrack;winTrack=winTrack->nextWindow)
{ // TODO this is currently still only using one prev track value.
winTrack->prevTrack = prevTrack; // Important for keeping track of conditional center labels!
}
// ORIG pixHeight += trackPlusLabelHeight(track, fontHeight);
if (!theImgBox) // prevTrack may have altered the height, so recalc height
setFlatTrackMaxHeight(flatTrack, fontHeight);
pixHeight += flatTrack->maxHeight;
prevTrack = track;
}
}
// allocate hvg png of pixWidth, pixHeight
imagePixelHeight = pixHeight;
if (psOutput)
{
hvg = hvGfxOpenPostScript(pixWidth, pixHeight, psOutput);
hvgSide = hvg; // Always only one image
}
else
{
boolean transparentImage = FALSE;
if (theImgBox!=NULL)
transparentImage = TRUE; // transparent because BG (blue ruler lines) is separate image
if (measureTiming)
measureTime("Time at start of obtaining trash hgt png image file");
trashDirFile(&pngTn, "hgt", "hgt", ".png");
hvg = hvGfxOpenPng(pixWidth, pixHeight, pngTn.forCgi, transparentImage);
if (theImgBox)
{
// Adds one single image for all tracks (COULD: build the track by track images)
theOneImg = imgBoxImageAdd(theImgBox,pngTn.forHtml,NULL,pixWidth, pixHeight,FALSE);
theSideImg = theOneImg; // Unlkess this is overwritten below, there is a single image
}
hvgSide = hvg; // Unless this is overwritten below, there is a single image
if (theImgBox && theImgBox->showPortal && withLeftLabels)
{
// TODO: It would be great to make the two images smaller,
// but keeping both the same full size for now
struct tempName pngTnSide;
trashDirFile(&pngTnSide, "hgtSide", "side", ".png");
hvgSide = hvGfxOpenPng(pixWidth, pixHeight, pngTnSide.forCgi, transparentImage);
// Also add the side image
theSideImg = imgBoxImageAdd(theImgBox,pngTnSide.forHtml,NULL,pixWidth, pixHeight,FALSE);
hvgSide->rc = revCmplDisp;
initColors(hvgSide);
}
}
hvg->rc = revCmplDisp;
initColors(hvg);
/* Start up client side map. */
hPrintf("\n");
// turn off inPlaceUpdate when rows in imgTbl can arbitrarily reappear and disappear (see redmine #7306 and #6944)
jsonObjectAdd(jsonForClient, "inPlaceUpdate", newJsonBoolean(withLeftLabels && withCenterLabels));
jsonObjectAdd(jsonForClient, "rulerClickHeight", newJsonNumber(rulerClickHeight));
if(newWinWidth)
{
jsonObjectAdd(jsonForClient, "newWinWidth", newJsonNumber(newWinWidth));
}
/* Save out picture and tell html file about it. */
if (hvgSide != hvg)
hvGfxClose(&hvgSide);
hvGfxClose(&hvg);
if (measureTiming)
measureTime("Time completed writing trash hgt png image file");
#ifdef SUPPORT_CONTENT_TYPE
char *type = cartUsualString(cart, "hgt.contentType", "html");
if(sameString(type, "jsonp"))
{
struct jsonElement *json = newJsonObject(newHash(8));
printf("Content-Type: application/json\n\n");
errAbortSetDoContentType(FALSE);
jsonObjectAdd(json, "track", newJsonString(cartString(cart, "hgt.trackNameFilter")));
jsonObjectAdd(json, "height", newJsonNumber(pixHeight));
jsonObjectAdd(json, "width", newJsonNumber(pixWidth));
jsonObjectAdd(json, "img", newJsonString(pngTn.forHtml));
printf("%s(", cartString(cart, "jsonp"));
hPrintEnable();
jsonPrint((struct jsonElement *) json, NULL, 0);
hPrintDisable();
printf(")\n");
return;
}
else if(sameString(type, "png") || sameString(type, "pdf") || sameString(type, "eps"))
{
// following code bypasses html and return png's directly - see redmine 4888
char *file;
if(sameString(type, "pdf"))
{
printf("Content-Disposition: filename=hgTracks.pdf\nContent-Type: application/pdf\n\n");
file = convertEpsToPdf(psOutput);
unlink(psOutput);
}
else if(sameString(type, "eps"))
{
printf("Content-Disposition: filename=hgTracks.eps\nContent-Type: application/eps\n\n");
file = psOutput;
}
else
{
printf("Content-Disposition: filename=hgTracks.png\nContent-Type: image/png\n\n");
file = pngTn.forCgi;
}
char buf[4096];
FILE *fd = fopen(file, "r");
if(fd == NULL)
// fail some other way (e.g. HTTP 500)?
errAbort("Couldn't open png for reading");
while (TRUE)
{
size_t n = fread(buf, 1, sizeof(buf), fd);
if(n)
fwrite(buf, 1, n, stdout);
else
break;
}
fclose(fd);
unlink(file);
return;
}
#endif///def SUPPORT_CONTENT_TYPE
if (theImgBox)
{
imageBoxDraw(theImgBox);
// If a portal was established, then set the global dimensions back to the portal size
if (imgBoxPortalDimensions(theImgBox,NULL,NULL,NULL,NULL,&virtWinStart,&virtWinEnd,&(tl.picWidth),NULL))
{
pixWidth = tl.picWidth;
virtWinBaseCount = virtWinEnd - virtWinStart;
fullInsideWidth = tl.picWidth - gfxBorder - fullInsideX;
}
imgBoxFree(&theImgBox);
}
else
{
char *titleAttr = "title='click or drag mouse in base position track to zoom in'";
hPrintf(" \n");
}
flatTracksFree(&flatTracks);
}
void makeHgGenomeTrackVisible(struct track *track)
/* This turns on a track clicked from hgGenome, even if it was previously */
/* hidden manually and there are cart vars to support that. */
{
struct hashEl *hels;
struct hashEl *hel;
char prefix[SMALLBUF];
/* First check if the click was from hgGenome. If not, leave. */
/* get the names of the tracks in the cart */
safef(prefix, sizeof(prefix), "%s_", hggGraphPrefix);
hels = cartFindPrefix(cart, prefix);
/* loop through them and compare them to the track passed into this */
/* function. */
for (hel = hels; hel != NULL; hel = hel->next)
{
struct trackDb *subtrack;
char *subtrackName = hel->val;
/* check non-subtrack. */
if (sameString(track->tdb->track, subtrackName))
{
track->visibility = tvFull;
track->tdb->visibility = tvFull;
cartSetString(cart, track->tdb->track, "full");
}
else if (track->tdb->subtracks != NULL)
{
struct slRef *tdbRef, *tdbRefList = trackDbListGetRefsToDescendants(track->tdb->subtracks);
for (tdbRef = tdbRefList; tdbRef != NULL; tdbRef = tdbRef->next)
{
subtrack = tdbRef->val;
if (sameString(subtrack->track, subtrackName))
{
char selName[SMALLBUF];
track->visibility = tvFull;
cartSetString(cart, track->tdb->track, "full");
track->tdb->visibility = tvFull;
subtrack->visibility = tvFull;
safef(selName, sizeof(selName), "%s_sel", subtrackName);
cartSetBoolean(cart, selName, TRUE);
}
}
slFreeList(&tdbRefList);
}
}
hashElFreeList(&hels);
}
struct sqlConnection *remoteTrackConnection(struct track *tg)
/* Get a connection to remote database as specified in remoteSql settings... */
{
if (!tg->isRemoteSql)
{
internalErr();
return NULL;
}
else
{
return sqlConnectRemote(tg->remoteSqlHost, tg->remoteSqlUser, tg->remoteSqlPassword,
tg->remoteSqlDatabase);
}
}
void addTdbListToTrackList(struct trackDb *tdbList, char *trackNameFilter,
struct track **pTrackList)
/* Convert a list of trackDb's to tracks, and append these to trackList. */
{
struct trackDb *tdb, *next;
struct track *track;
TrackHandler handler;
tdbSortPrioritiesFromCart(cart, &tdbList);
for (tdb = tdbList; tdb != NULL; tdb = next)
{
next = tdb->next;
if(trackNameFilter != NULL && strcmp(trackNameFilter, tdb->track))
// suppress loading & display of all tracks except for the one passed in via trackNameFilter
continue;
if (sameString(tdb->type, "downloadsOnly")) // These tracks should not even be seen by hgTracks.
continue;
track = trackFromTrackDb(tdb);
track->hasUi = TRUE;
if (slCount(tdb->subtracks) != 0)
{
tdbSortPrioritiesFromCart(cart, &(tdb->subtracks));
if (trackDbLocalSetting(tdb, "compositeTrack"))
makeCompositeTrack(track, tdb);
else if (trackDbLocalSetting(tdb, "container"))
makeContainerTrack(track, tdb);
}
else
{
handler = lookupTrackHandlerClosestToHome(tdb);
if (handler != NULL)
handler(track);
}
if (cgiVarExists("hgGenomeClick"))
makeHgGenomeTrackVisible(track);
if (track->loadItems == NULL)
warn("No load handler for %s; possible missing trackDb `type' or `subTrack' attribute", tdb->track);
else if (track->drawItems == NULL)
warn("No draw handler for %s", tdb->track);
else
slAddHead(pTrackList, track);
}
}
void loadFromTrackDb(struct track **pTrackList)
/* Load tracks from database, consulting handler list. */
{
char *trackNameFilter = cartOptionalString(cart, "hgt.trackNameFilter");
struct trackDb *tdbList;
if(trackNameFilter == NULL)
tdbList = hTrackDb(database);
else
{
tdbList = hTrackDbForTrack(database, trackNameFilter);
if (tdbList && tdbList->parent) // we want to give the composite parent a chance to load and set options
{
while(tdbList->parent)
{
if (tdbList->parent->subtracks == NULL) // we don't want to go up to a supertrack
break;
tdbList = tdbList->parent;
}
trackNameFilter = tdbList->track;
}
}
addTdbListToTrackList(tdbList, trackNameFilter, pTrackList);
}
static int getScoreFilter(char *trackName)
/* check for score filter configuration setting */
{
char optionScoreStr[256];
safef(optionScoreStr, sizeof(optionScoreStr), "%s.scoreFilter", trackName);
return cartUsualInt(cart, optionScoreStr, 0);
}
void ctLoadSimpleBed(struct track *tg)
/* Load the items in one custom track - just move beds in
* window... */
{
struct customTrack *ct = tg->customPt;
struct bed *bed, *nextBed, *list = NULL;
int scoreFilter = getScoreFilter(ct->tdb->track);
if (ct->dbTrack)
{
int fieldCount = ct->fieldCount;
int rowOffset;
char **row;
struct sqlConnection *conn =
hAllocConn(CUSTOM_TRASH);
struct sqlResult *sr = NULL;
sr = hRangeQuery(conn, ct->dbTableName, chromName, winStart, winEnd,
NULL, &rowOffset);
while ((row = sqlNextRow(sr)) != NULL)
{
bed = bedLoadN(row+rowOffset, fieldCount);
if (scoreFilter && bed->score < scoreFilter)
continue;
slAddHead(&list, bed);
}
hFreeConn(&conn);
}
else
{
for (bed = ct->bedList; bed != NULL; bed = nextBed)
{
nextBed = bed->next;
if (bed->chromStart < winEnd && bed->chromEnd > winStart
&& sameString(chromName, bed->chrom))
{
if (scoreFilter && bed->score < scoreFilter)
continue;
slAddHead(&list, bed);
}
}
}
slSort(&list, bedCmp);
tg->items = list;
}
void ctLoadBed9(struct track *tg)
/* Convert bed info in window to linked feature. */
{
struct customTrack *ct = tg->customPt;
struct bed *bed;
struct linkedFeatures *lfList = NULL, *lf;
boolean useItemRgb = FALSE;
int scoreFilter = getScoreFilter(ct->tdb->track);
useItemRgb = bedItemRgb(ct->tdb);
if (ct->dbTrack)
{
int rowOffset;
char **row;
struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
struct sqlResult *sr = NULL;
sr = hRangeQuery(conn, ct->dbTableName, chromName, winStart, winEnd,
NULL, &rowOffset);
while ((row = sqlNextRow(sr)) != NULL)
{
bed = bedLoadN(row+rowOffset, 9);
if (scoreFilter && bed->score < scoreFilter)
continue;
bed8To12(bed);
lf = lfFromBed(bed);
if (useItemRgb)
{
lf->extra = (void *)USE_ITEM_RGB; /* signal for coloring */
lf->filterColor=bed->itemRgb;
}
slAddHead(&lfList, lf);
}
hFreeConn(&conn);
}
else
{
for (bed = ct->bedList; bed != NULL; bed = bed->next)
{
if (scoreFilter && bed->score < scoreFilter)
continue;
if (bed->chromStart < winEnd && bed->chromEnd > winStart
&& sameString(chromName, bed->chrom))
{
bed8To12(bed);
lf = lfFromBed(bed);
if (useItemRgb)
{
lf->extra = (void *)USE_ITEM_RGB; /* signal for coloring */
lf->filterColor=bed->itemRgb;
}
slAddHead(&lfList, lf);
}
}
}
slReverse(&lfList);
slSort(&lfList, linkedFeaturesCmp);
tg->items = lfList;
}
void ctLoadBed8(struct track *tg)
/* Convert bed info in window to linked feature. */
{
struct customTrack *ct = tg->customPt;
struct bed *bed;
struct linkedFeatures *lfList = NULL, *lf;
int scoreFilter = getScoreFilter(ct->tdb->track);
if (ct->dbTrack)
{
int fieldCount = ct->fieldCount;
int rowOffset;
char **row;
struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
struct sqlResult *sr = NULL;
sr = hRangeQuery(conn, ct->dbTableName, chromName, winStart, winEnd,
NULL, &rowOffset);
while ((row = sqlNextRow(sr)) != NULL)
{
bed = bedLoadN(row+rowOffset, fieldCount);
if (scoreFilter && bed->score < scoreFilter)
continue;
bed8To12(bed);
lf = lfFromBed(bed);
slAddHead(&lfList, lf);
}
hFreeConn(&conn);
}
else
{
for (bed = ct->bedList; bed != NULL; bed = bed->next)
{
if (scoreFilter && bed->score < scoreFilter)
continue;
if (bed->chromStart < winEnd && bed->chromEnd > winStart
&& sameString(chromName, bed->chrom))
{
bed8To12(bed);
lf = lfFromBed(bed);
slAddHead(&lfList, lf);
}
}
}
slReverse(&lfList);
slSort(&lfList, linkedFeaturesCmp);
tg->items = lfList;
}
void ctLoadGappedBed(struct track *tg)
/* Convert bed info in window to linked feature. */
{
struct customTrack *ct = tg->customPt;
struct bed *bed;
struct linkedFeatures *lfList = NULL, *lf;
boolean useItemRgb = FALSE;
int scoreFilter = getScoreFilter(ct->tdb->track);
useItemRgb = bedItemRgb(ct->tdb);
if (ct->dbTrack)
{
int fieldCount = ct->fieldCount;
int rowOffset;
char **row;
struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
struct sqlResult *sr = NULL;
sr = hRangeQuery(conn, ct->dbTableName, chromName, winStart, winEnd,
NULL, &rowOffset);
while ((row = sqlNextRow(sr)) != NULL)
{
bed = bedLoadN(row+rowOffset, fieldCount);
lf = lfFromBed(bed);
if (scoreFilter && bed->score < scoreFilter)
continue;
if (useItemRgb)
{
lf->extra = (void *)USE_ITEM_RGB; /* signal for coloring */
lf->filterColor=bed->itemRgb;
}
slAddHead(&lfList, lf);
}
hFreeConn(&conn);
}
else
{
for (bed = ct->bedList; bed != NULL; bed = bed->next)
{
if (scoreFilter && bed->score < scoreFilter)
continue;
if (bed->chromStart < winEnd && bed->chromEnd > winStart
&& sameString(chromName, bed->chrom))
{
lf = lfFromBed(bed);
if (useItemRgb)
{
lf->extra = (void *)USE_ITEM_RGB; /* signal for coloring */
lf->filterColor=bed->itemRgb;
}
slAddHead(&lfList, lf);
}
}
}
slReverse(&lfList);
slSort(&lfList, linkedFeaturesCmp);
tg->items = lfList;
}
void ctLoadColoredExon(struct track *tg)
/* Convert bed info in window to linked features series for custom track. */
{
struct customTrack *ct = tg->customPt;
struct bed *bed;
struct linkedFeaturesSeries *lfsList = NULL, *lfs;
if (ct->dbTrack)
{
int fieldCount = ct->fieldCount;
int rowOffset;
char **row;
struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
struct sqlResult *sr = NULL;
sr = hRangeQuery(conn, ct->dbTableName, chromName, winStart, winEnd,
NULL, &rowOffset);
while ((row = sqlNextRow(sr)) != NULL)
{
bed = bedLoadN(row+rowOffset, fieldCount);
lfs = lfsFromColoredExonBed(bed);
slAddHead(&lfsList, lfs);
}
hFreeConn(&conn);
}
else
{
for (bed = ct->bedList; bed != NULL; bed = bed->next)
{
if (bed->chromStart < winEnd && bed->chromEnd > winStart
&& sameString(chromName, bed->chrom))
{
lfs = lfsFromColoredExonBed(bed);
slAddHead(&lfsList, lfs);
}
}
}
slReverse(&lfsList);
slSort(&lfsList, linkedFeaturesSeriesCmp);
tg->items = lfsList;
}
char *ctMapItemName(struct track *tg, void *item)
/* Return composite item name for custom tracks. */
{
char *itemName = tg->itemName(tg, item);
static char buf[512];
if (strlen(itemName) > 0)
safef(buf, sizeof(buf), "%s %s", ctFileName, itemName);
else
safef(buf, sizeof(buf), "%s NoItemName", ctFileName);
return buf;
}
void coloredExonMethodsFromCt(struct track *tg)
/* same as coloredExonMethods but different loader. */
{
linkedFeaturesSeriesMethods(tg);
tg->loadItems = ctLoadColoredExon;
tg->canPack = TRUE;
}
void dontLoadItems(struct track *tg)
/* No-op loadItems when we aren't going to try. */
{
}
struct track *newCustomTrack(struct customTrack *ct)
/* Make up a new custom track. */
{
struct track *tg = NULL;
struct trackDb *tdb = ct->tdb;
boolean useItemRgb = FALSE;
char *typeOrig = tdb->type;
char *typeDupe = cloneString(typeOrig);
char *typeParam = typeDupe;
char *type = nextWord(&typeParam);
if (ct->dbTrack)
{
// make sure we can connect
struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
hFreeConn(&conn);
}
useItemRgb = bedItemRgb(tdb);
if (sameString(type, "maf"))
{
tg = trackFromTrackDb(tdb);
tg->canPack = TRUE;
wigMafMethods(tg, tdb, 0, NULL);
if (!ct->dbTrack)
errAbort("custom maf tracks must be in database");
struct mafPriv *mp;
AllocVar(mp);
mp->ct = ct;
tg->customPt = mp;
tg->nextItemButtonable = FALSE;
}
else if (sameString(type, "wig"))
{
tg = trackFromTrackDb(tdb);
if (ct->dbTrack)
tg->loadItems = wigLoadItems;
else
tg->loadItems = ctWigLoadItems;
tg->customPt = ct;
tg->nextItemButtonable = FALSE;
}
else if (sameString(type, "bigWig"))
{
tg = trackFromTrackDb(tdb);
tg->bbiFile = ct->bbiFile;
tg->nextItemButtonable = FALSE;
if (trackShouldUseAjaxRetrieval(tg))
tg->loadItems = dontLoadItems;
}
else if (sameString(type, "bigBed")|| sameString(type, "bigGenePred") ||
sameString(type, "bigNarrowPeak") || sameString(type, "bigPsl") ||
sameString(type, "bigMaf")|| sameString(type, "bigChain") ||
sameString(type, "bigLolly") ||
sameString(type, "bigBarChart") || sameString(type, "bigInteract"))
{
struct bbiFile *bbi = ct->bbiFile;
/* Find field counts, and from that revise the tdb->type to be more complete. */
char extra = (bbi->fieldCount > bbi->definedFieldCount ? '+' : '.');
char typeBuf[64];
if (sameString(type, "bigGenePred"))
safef(typeBuf, sizeof(typeBuf), "bigGenePred");
else if (sameString(type, "bigNarrowPeak"))
safef(typeBuf, sizeof(typeBuf), "bigNarrowPeak");
else if (sameString(type, "bigChain"))
safef(typeBuf, sizeof(typeBuf), "bigChain");
else if (sameString(type, "bigMaf"))
safef(typeBuf, sizeof(typeBuf), "bigMaf");
else if (sameString(type, "bigPsl"))
safef(typeBuf, sizeof(typeBuf), "bigPsl");
else if (sameString(type, "bigBarChart"))
safef(typeBuf, sizeof(typeBuf), "bigBarChart");
else if (sameString(type, "bigLolly"))
safef(typeBuf, sizeof(typeBuf), "bigLolly");
else if (sameString(type, "bigInteract"))
safef(typeBuf, sizeof(typeBuf), "bigInteract");
else
safef(typeBuf, sizeof(typeBuf), "bigBed %d %c", bbi->definedFieldCount, extra);
tdb->type = cloneString(typeBuf);
/* Finish wrapping track around tdb. */
tg = trackFromTrackDb(tdb);
tg->bbiFile = bbi;
tg->nextItemButtonable = TRUE;
if (trackShouldUseAjaxRetrieval(tg))
tg->loadItems = dontLoadItems;
}
else if (sameString(type, "bedGraph"))
{
tg = trackFromTrackDb(tdb);
tg->canPack = FALSE;
tg->customPt = ct;
ct->wigFile = ctFileName;
tg->mapItemName = ctMapItemName;
tg->nextItemButtonable = FALSE;
}
else if (sameString(type, "bed"))
{
tg = trackFromTrackDb(tdb);
if (ct->fieldCount < 8)
{
tg->loadItems = ctLoadSimpleBed;
}
else if (useItemRgb && ct->fieldCount == 9)
{
tg->loadItems = ctLoadBed9;
}
else if (ct->fieldCount < 12)
{
tg->loadItems = ctLoadBed8;
}
else if (ct->fieldCount == 15)
{
char *theType = trackDbSetting(tdb, "type");
if (theType && sameString(theType, "expRatio"))
{
tg = trackFromTrackDb(tdb);
expRatioMethodsFromCt(tg);
}
else
tg->loadItems = ctLoadGappedBed;
}
else
{
tg->loadItems = ctLoadGappedBed;
}
tg->mapItemName = ctMapItemName;
tg->canPack = TRUE;
tg->nextItemButtonable = TRUE;
tg->customPt = ct;
}
else if (sameString(type, "chromGraph"))
{
tdb->type = NULL; /* Swap out type for the moment. */
tg = trackFromTrackDb(tdb);
chromGraphMethodsCt(tg);
tg->nextItemButtonable = FALSE;
tdb->type = typeOrig;
}
else if (sameString(type, "array"))
{
tg = trackFromTrackDb(tdb);
expRatioMethodsFromCt(tg);
tg->nextItemButtonable = TRUE;
tg->customPt = ct;
}
else if (sameString(type, "coloredExon"))
{
tg = trackFromTrackDb(tdb);
coloredExonMethodsFromCt(tg);
tg->nextItemButtonable = TRUE;
tg->customPt = ct;
}
else if (sameString(type, "encodePeak"))
{
tg = trackFromTrackDb(tdb);
encodePeakMethodsCt(tg);
tg->nextItemButtonable = TRUE;
tg->customPt = ct;
}
else if (sameString(type, "bam"))
{
tg = trackFromTrackDb(tdb);
tg->customPt = ct;
bamMethods(tg);
if (trackShouldUseAjaxRetrieval(tg))
tg->loadItems = dontLoadItems;
tg->mapItemName = ctMapItemName;
}
else if (sameString(type, "vcfTabix"))
{
tg = trackFromTrackDb(tdb);
tg->customPt = ct;
vcfTabixMethods(tg);
if (trackShouldUseAjaxRetrieval(tg))
tg->loadItems = dontLoadItems;
tg->mapItemName = ctMapItemName;
}
else if (sameString(type, "vcf"))
{
tg = trackFromTrackDb(tdb);
tg->customPt = ct;
vcfMethods(tg);
tg->mapItemName = ctMapItemName;
}
else if (sameString(type, "makeItems"))
{
tg = trackFromTrackDb(tdb);
makeItemsMethods(tg);
tg->nextItemButtonable = TRUE;
tg->customPt = ct;
}
else if (sameString(type, "bedTabix") || sameString(type, "longTabix"))
{
knetUdcInstall();
tg = trackFromTrackDb(tdb);
tg->customPt = ct;
tg->mapItemName = ctMapItemName; /* must be here to see ctMapItemName */
}
else if (sameString(type, "bedDetail"))
{
tg = trackFromTrackDb(tdb);
bedDetailCtMethods(tg, ct);
tg->mapItemName = ctMapItemName; /* must be here to see ctMapItemName */
}
else if (sameString(type, "adjacency"))
{
extern void adjacencyMethods(struct track *track);
tg = trackFromTrackDb(tdb);
adjacencyMethods(tg);
//tg->mapItemName = ctMapItemName;
tg->customPt = ct;
}
else if (sameString(type, "pgSnp"))
{
tg = trackFromTrackDb(tdb);
pgSnpCtMethods(tg);
//tg->mapItemName = ctMapItemName;
tg->customPt = ct;
}
else if (sameString(type, "barChart"))
{
tg = trackFromTrackDb(tdb);
barChartCtMethods(tg);
tg->customPt = ct;
}
else if (sameString(type, "interact"))
{
tg = trackFromTrackDb(tdb);
interactCtMethods(tg);
tg->customPt = ct;
}
else if (sameString(type, "hic"))
{
tg = trackFromTrackDb(tdb);
hicCtMethods(tg);
tg->customPt = ct;
}
else
{
errAbort("Unrecognized custom track type %s", type);
}
tg->hasUi = TRUE;
tg->customTrack = TRUE;// Explicitly declare this a custom track for flatTrack ordering
freez(&typeDupe);
return tg;
}
char *getPositionFromCustomTracks()
/* Parses custom track data to get the position variable
* return - The first chromosome position variable found in the
* custom track data. */
{
char *pos = NULL;
struct slName *bl = NULL;
ctList = customTracksParseCart(database, cart, &browserLines, &ctFileName);
#ifdef NOT
/* this is the incorrect location for this to be done. This is actually
* counting all custom tracks whether they are new or not. Every view of
* the tracks adds to the penalty.
*/
if (slCount(ctList) > 0) {
int trackCount = slCount(ctList);
/* add penalty in relation to number of tracks created, and adjust
* exitMs accordingly so that it will not hogExit at this time
*/
(void) earlyBotCheck(enteredMainTime, "hgTracks", (double)(trackCount + 1) * delayFraction, warnMs, (trackCount + 1)*exitMs);
}
#endif
for (bl = browserLines; bl != NULL; bl = bl->next)
{
char *words[96];
int wordCount;
char *dupe = cloneString(bl->name);
wordCount = chopLine(dupe, words);
if (wordCount >= 3)
{
char *command = words[1];
if (sameString(command, "position"))
pos = cloneString(words[2]);
}
freez(&dupe);
if (pos != NULL)
break;
}
return pos;
}
void loadCustomTracks(struct track **pTrackList)
/* Load up custom tracks and append to list. */
{
struct customTrack *ct;
struct track *tg;
struct slName *bl;
/* build up browser lines from cart variables set by hgCustom */
char *visAll = cartCgiUsualString(cart, "hgt.visAllFromCt", NULL);
if (visAll)
{
char buf[SMALLBUF];
safef(buf, sizeof buf, "browser %s %s", visAll, "all");
slAddTail(&browserLines, slNameNew(buf));
}
struct hashEl *visEl;
struct hashEl *visList = cartFindPrefix(cart, "hgtct.");
for (visEl = visList; visEl != NULL; visEl = visEl->next)
{
char buf[256];
safef(buf, sizeof buf, "browser %s %s", cartString(cart, visEl->name),
chopPrefix(cloneString(visEl->name)));
slAddTail(&browserLines, slNameNew(buf));
cartRemove(cart, visEl->name);
}
hashElFreeList(&visList);
/* The loading is now handled by getPositionFromCustomTracks(). */
/* Process browser commands in custom track. */
for (bl = browserLines; bl != NULL; bl = bl->next)
{
char *words[96];
int wordCount;
wordCount = chopLine(bl->name, words);
if (wordCount > 1)
{
char *command = words[1];
if (sameString(command, "hide")
|| sameString(command, "dense")
|| sameString(command, "pack")
|| sameString(command, "squish")
|| sameString(command, "full"))
{
if (wordCount > 2)
{
int i;
for (i=2; inext)
{
if (toAll || sameString(s, tg->track))
{
if (hTvFromString(command) == tg->tdb->visibility)
/* remove if setting to default vis */
cartRemove(cart, tg->track);
else
cartSetString(cart, tg->track, command);
/* hide or show supertrack enclosing this track */
if (tdbIsSuperTrackChild(tg->tdb))
{
assert(tg->tdb->parentName != NULL);
cartSetString(cart, tg->tdb->parentName,
(sameString(command, "hide") ?
"hide" : "show"));
}
}
}
}
}
}
else if (sameString(command, "position"))
{
if (wordCount < 3)
errAbort("Expecting 3 words in browser position line");
if (!hgIsChromRange(database, words[2]))
errAbort("browser position needs to be in chrN:123-456 format");
hgParseChromRange(database, words[2], &chromName, &winStart, &winEnd);
/*Fix a start window of -1 that is returned when a custom track position
begins at 0
*/
if (winStart < 0)
{
winStart = 0;
}
}
else if (sameString(command, "pix"))
{
if (wordCount != 3)
errAbort("Expecting 3 words in pix line");
trackLayoutSetPicWidth(&tl, words[2]);
}
}
}
for (ct = ctList; ct != NULL; ct = ct->next)
{
hasCustomTracks = TRUE;
tg = newCustomTrack(ct);
slAddHead(pTrackList, tg);
}
}
void loadTrackHubs(struct track **pTrackList, struct grp **pGrpList)
/* Load up stuff from data hubs and append to lists. */
{
struct trackDb *tdbList = hubCollectTracks(database, pGrpList);
addTdbListToTrackList(tdbList, NULL, pTrackList);
}
boolean restrictionEnzymesOk()
/* Check to see if it's OK to do restriction enzymes. */
{
return (sqlDatabaseExists("hgFixed") && hTableExists("hgFixed", "cutters") &&
hTableExists("hgFixed", "rebaseRefs") &&
hTableExists("hgFixed", "rebaseCompanies"));
}
static void setSuperTrackHasVisibleMembers(struct trackDb *tdb)
/* Determine if any member tracks are visible -- currently
* recording this in the parent's visibility setting */
{
tdb->visibility = tvDense;
}
boolean superTrackHasVisibleMembers(struct trackDb *tdb)
/* Determine if any member tracks are visible -- currently
* recording this in the parent's visibility setting */
{
if (!tdbIsSuper(tdb))
return FALSE;
return (tdb->visibility != tvHide);
}
int hubCmpAlpha(const void *va, const void *vb)
/* Compare to sort hubs based on name */
{
const struct trackHub *a = *((struct trackHub **)va);
const struct trackHub *b = *((struct trackHub **)vb);
return strcmp(a->shortLabel, b->shortLabel);
}
static void rPropagateGroup(struct track *track, struct group *group)
// group should spread to multiple levels of children.
{
struct track *subtrack = track->subtracks;
for ( ;subtrack != NULL;subtrack = subtrack->next)
{
subtrack->group = group;
rPropagateGroup(subtrack, group);
}
}
static void groupTracks(struct track **pTrackList,
struct group **pGroupList, struct grp *grpList, int vis)
/* Make up groups and assign tracks to groups.
* If vis is -1, restore default groups to tracks. */
{
struct group *unknown = NULL;
struct group *group, *list = NULL;
struct hash *hash = newHash(8);
struct track *track;
struct trackRef *tr;
struct grp* grps = hLoadGrps(database);
struct grp *grp;
float minPriority = 100000; // something really large
boolean foundMap = FALSE;
/* build group objects from database. */
for (grp = grps; grp != NULL; grp = grp->next)
{
if (sameString(grp->name, "map"))
foundMap = TRUE;
/* deal with group reordering */
float priority = grp->priority;
// we want to get the minimum priority over 1 (which is custom tracks)
if ((priority > 1.0) && (priority < minPriority)) minPriority = priority;
if (withPriorityOverride)
{
char cartVar[512];
safef(cartVar, sizeof(cartVar), "%s.priority",grp->name);
if (vis != -1)
priority = (float)cartUsualDouble(cart, cartVar, grp->priority);
if (priority == grp->priority)
cartRemove(cart, cartVar);
}
/* create group object; add to list and hash */
AllocVar(group);
group->name = cloneString(grp->name);
group->label = cloneString(grp->label);
group->defaultPriority = grp->priority;
group->priority = priority;
group->defaultIsClosed = grp->defaultIsClosed;
slAddHead(&list, group);
hashAdd(hash, grp->name, group);
}
grpFreeList(&grps);
double priorityInc;
double priority = 1.00001;
if (grpList)
{
minPriority -= 1.0; // priority is 1-based
// the idea here is to get enough room between priority 1
// (which is custom tracks) and the group with the next
// priority number, so that the hub nestle inbetween the
// custom tracks and everything else at the top of the list
// of track groups
priorityInc = (0.9 * minPriority) / slCount(grpList);
priority = 1.0 + priorityInc;
}
for(; grpList; grpList = grpList->next)
{
AllocVar(group);
group->name = cloneString(grpList->name);
group->label = cloneString(grpList->label);
group->defaultPriority = group->priority = priority;
priority += priorityInc;
slAddHead(&list, group);
hashAdd(hash, group->name, group);
}
//
// If there isn't a map group, make one and set the priority so it will be right after custom tracks
// and hub groups.
if (!foundMap)
{
AllocVar(group);
group->name = cloneString("map");
group->label = cloneString("Mapping and Sequencing");
group->defaultPriority = priority;
group->priority = priority;
group->defaultIsClosed = FALSE;
slAddHead(&list, group);
hashAdd(hash, "map", group);
}
/* Loop through tracks and fill in their groups.
* If necessary make up an unknown group. */
for (track = *pTrackList; track != NULL; track = track->next)
{
/* handle track reordering feature -- change group assigned to track */
if (withPriorityOverride)
{
char *groupName = NULL;
char cartVar[256];
/* belt and suspenders -- accomodate inconsistent track/trackDb
* creation. Note -- with code cleanup, these default variables
* could be retired, and the tdb versions used as defaults */
if (!track->defaultGroupName)
{
if (track->tdb && track->tdb->grp)
track->defaultGroupName = cloneString(track->tdb->grp);
else
track->defaultGroupName = cloneString("other");
}
if (tdbIsSuperTrackChild(track->tdb))
{
assert(track->tdb->parentName != NULL);
/* supertrack member must be in same group as its super */
/* determine supertrack group */
safef(cartVar, sizeof(cartVar), "%s.group",track->tdb->parentName);
groupName = cloneString( //1
cartUsualString(cart, cartVar, track->tdb->parent->grp));
track->tdb->parent->grp = cloneString(groupName); //2
}
else
{
/* get group */
safef(cartVar, sizeof(cartVar), "%s.group",track->track);
groupName = cloneString( //1
cartUsualString(cart, cartVar, track->defaultGroupName));
}
if (vis == -1)
groupName = track->defaultGroupName; //0
track->groupName = cloneString(groupName); // wasting a few clones! //3
if (sameString(groupName, track->defaultGroupName))
cartRemove(cart, cartVar);
/* get priority */
safef(cartVar, sizeof(cartVar), "%s.priority",track->track);
float priority = (float)cartUsualDouble(cart, cartVar,
track->defaultPriority);
/* remove cart variables that are the same as the trackDb settings */
/* UGLY - add me back when tdb->priority is no longer pre-clobbered by cart var value
if (priority == track->defaultPriority)
cartRemove(cart, cartVar);
*/
track->priority = priority;
}
/* assign group object to track */
if (track->groupName == NULL)
group = NULL;
else
group = hashFindVal(hash, track->groupName);
if (group == NULL)
{
if (unknown == NULL)
{
AllocVar(unknown);
unknown->name = cloneString("other");
unknown->label = cloneString("other");
unknown->priority = 1000000;
slAddHead(&list, unknown);
}
group = unknown;
}
track->group = group;
rPropagateGroup(track, group);
}
/* Sort tracks by combined group/track priority, and
* then add references to track to group. */
slSort(pTrackList, tgCmpPriority);
for (track = *pTrackList; track != NULL; track = track->next)
{
AllocVar(tr);
tr->track = track;
slAddHead(&track->group->trackList, tr);
}
/* Straighten things out, clean up, and go home. */
for (group = list; group != NULL; group = group->next)
slReverse(&group->trackList);
slSort(&list, gCmpPriority);
hashFree(&hash);
*pGroupList = list;
}
void groupTrackListAddSuper(struct cart *cart, struct group *group)
/* Construct a new track list that includes supertracks, sort by priority,
* and determine if supertracks have visible members.
* Replace the group track list with this new list.
* Shared by hgTracks and configure page to expand track list,
* in contexts where no track display functions (which don't understand
* supertracks) are invoked. */
{
struct trackRef *newList = NULL, *tr, *ref;
struct hash *superHash = hashNew(8);
if (!group || !group->trackList)
return;
for (tr = group->trackList; tr != NULL; tr = tr->next)
{
struct track *track = tr->track;
AllocVar(ref);
ref->track = track;
slAddHead(&newList, ref);
if (tdbIsSuperTrackChild(track->tdb))
{
assert(track->tdb->parentName != NULL);
if (hTvFromString(cartUsualString(cart, track->track,
hStringFromTv(track->tdb->visibility))) != tvHide)
setSuperTrackHasVisibleMembers(track->tdb->parent);
assert(track->parent == NULL);
track->parent = hashFindVal(superHash, track->tdb->parentName);
if (track->parent)
continue;
/* create track and reference for the supertrack */
struct track *superTrack = track->parent = trackFromTrackDb(track->tdb->parent);
track->parent = superTrack;
if (trackHash != NULL)
hashAddUnique(trackHash,superTrack->track,superTrack);
superTrack->hasUi = TRUE;
superTrack->group = group;
superTrack->groupName = cloneString(group->name);
superTrack->defaultGroupName = cloneString(group->name);
/* handle track reordering */
char cartVar[256];
safef(cartVar, sizeof(cartVar), "%s.priority",track->tdb->parentName);
float priority = (float)cartUsualDouble(cart, cartVar,
track->tdb->parent->priority);
/* remove cart variables that are the same as the trackDb settings */
if (priority == track->tdb->parent->priority)
cartRemove(cart, cartVar);
superTrack->priority = priority;
AllocVar(ref);
ref->track = superTrack;
slAddHead(&newList, ref);
hashAdd(superHash, track->tdb->parentName, superTrack);
}
}
slSort(&newList, trackRefCmpPriority);
hashFree(&superHash);
/* we could free the old track list here, but it's a trivial amount of mem */
group->trackList = newList;
}
void topButton(char *var, char *label)
/* create a 3 or 4-char wide button for top line of display.
* 3 chars wide for odd-length labels, 4 for even length.
* Pad with spaces so label is centered */
{
char paddedLabel[5] = " ";
int len = strlen(label);
if (len > 4)
{
/* truncate */
/* or maybe errabort ? */
label[3] = 0;
len = 4;
}
if (len % 2 != 0)
paddedLabel[3] = 0;
if (len == strlen(paddedLabel))
strcpy(paddedLabel, label);
else
{
int i;
for (i=0; itdb))
{
assert(track->tdb->parent != NULL);
if (sameString("hide", cartUsualString(cart, track->tdb->parent->track,
track->tdb->parent->isShow ? "show" : "hide")))
track->visibility = tvHide;
}
}
struct track *rFindTrackWithTable(char *tableName, struct track *trackList)
/* Recursively search through track list looking for first one that matches table. */
{
struct track *track;
for (track = trackList; track != NULL; track = track->next)
{
if (sameString(tableName, track->table))
return track;
struct track *subTrack = rFindTrackWithTable(tableName, track->subtracks);
if (subTrack != NULL)
return subTrack;
}
return NULL;
}
static void setSearchedTrackToPackOrFull(struct track *trackList)
// Open track associated with search position if any. Also open its parents if any.
{
if (NULL != hgp && NULL != hgp->tableList && NULL != hgp->tableList->name)
{
char *tableName = hgp->tableList->name;
struct track *matchTrack = rFindTrackWithTable(tableName, trackList);
if (matchTrack != NULL)
tdbSetCartVisibility(matchTrack->tdb, cart, hCarefulTrackOpenVis(database, matchTrack->track));
}
}
struct track *getTrackList( struct group **pGroupList, int vis)
/* Return list of all tracks, organizing by groups.
* If vis is -1, restore default groups to tracks.
* Shared by hgTracks and configure page. */
{
struct track *track, *trackList = NULL;
registerTrackHandlers();
/* Load regular tracks, blatted tracks, and custom tracks.
* Best to load custom last. */
loadFromTrackDb(&trackList);
if (measureTiming)
measureTime("Time after trackDbLoad ");
if (pcrResultParseCart(database, cart, NULL, NULL, NULL))
slSafeAddHead(&trackList, pcrResultTg());
if (userSeqString != NULL)
slSafeAddHead(&trackList, userPslTg());
slSafeAddHead(&trackList, oligoMatchTg());
if (restrictionEnzymesOk())
{
slSafeAddHead(&trackList, cuttersTg());
}
if (wikiTrackEnabled(database, NULL))
{
addWikiTrack(&trackList);
struct sqlConnection *conn = wikiConnect();
if (sqlTableExists(conn, "variome"))
addVariomeWikiTrack(&trackList);
wikiDisconnect(&conn);
}
struct grp *grpList = NULL;
if (cartOptionalString(cart, "hgt.trackNameFilter") == NULL)
{ // If a single track was asked for and it is from a hub, then it is already in trackList
loadTrackHubs(&trackList, &grpList);
}
loadCustomTracks(&trackList);
groupTracks( &trackList, pGroupList, grpList, vis);
setSearchedTrackToPackOrFull(trackList);
boolean hideTracks = cgiOptionalString( "hideTracks") != NULL;
if (hideTracks)
changeTrackVis(groupList, NULL, tvHide); // set all top-level tracks to hide
/* Get visibility values if any from ui. */
struct hash *superTrackHash = newHash(5); // cache whether supertrack is hiding tracks or not
char buffer[4096];
for (track = trackList; track != NULL; track = track->next)
{
// deal with any supertracks we're seeing for the first time
if (tdbIsSuperTrackChild(track->tdb))
{
struct hashEl *hel = NULL;
if ((hel = hashLookup(superTrackHash, track->tdb->parent->track)) == NULL) // we haven't seen this guy
{
// first deal with visibility of super track
char *s = hideTracks ? cgiOptionalString(track->tdb->parent->track) : cartOptionalString(cart, track->tdb->parent->track);
if (s)
{
track->tdb->parent->visibility = hTvFromString(s) ;
cartSetString(cart, track->tdb->parent->track, s);
}
else if (startsWith("hub_", track->tdb->parent->track))
{
s = hideTracks ? cgiOptionalString( trackHubSkipHubName(track->tdb->parent->track)) : cartOptionalString( cart, trackHubSkipHubName(track->tdb->parent->track));
if (s)
{
cartSetString(cart, track->tdb->parent->track, s);
cartRemove(cart, trackHubSkipHubName(track->tdb->parent->track)); // remove the undecorated version
track->tdb->parent->visibility = hTvFromString(s) ;
}
}
// now look to see if we have a _hideKids statement to turn off all subtracks (including the current one)
unsigned hideKids = 0;
char *usedThis = buffer;
safef(buffer, sizeof buffer, "%s_hideKids", track->tdb->parent->track);
s = cartOptionalString(cart, buffer);
if (s == NULL && startsWith("hub_", track->tdb->parent->track))
s = cartOptionalString(cart, usedThis = trackHubSkipHubName(buffer));
if (s != NULL)
{
hideKids = 1;
cartRemove(cart, usedThis); // we don't want this hanging out in the cart
}
// mark this as having been addressed
hel = hashAddInt(superTrackHash, track->tdb->parent->track, hideKids );
}
if ( ptToInt(hel->val) == 1) // we want to hide this track
{
if (tvHide == track->tdb->visibility)
/* remove if setting to default vis */
cartRemove(cart, track->track);
else
cartSetString(cart, track->track, "hide");
track->visibility = tvHide;
}
}
// we use cgiOptionString because the above code may have turned off the track in the cart if
// the user requested that all the default tracks be turned off
char *s = hideTracks ? cgiOptionalString(track->track) : cartOptionalString(cart, track->track);
if (s != NULL)
{
if (!track->limitedVisSet)
{
track->visibility = hTvFromString(s);
cartSetString(cart, track->track, s);
}
}
else
{
// maybe this track is on the URL without the hub_ prefix
if (startsWith("hub_", track->track))
s = cgiOptionalString(trackHubSkipHubName(track->track));
if (s != NULL && !track->limitedVisSet)
{
track->visibility = hTvFromString(s);
cartSetString(cart, track->track, s); // add the decorated visibility to the cart
cartRemove(cart, trackHubSkipHubName(track->track)); // remove the undecorated version
}
}
// now deal with composite track children
if (tdbIsComposite(track->tdb))
{
char *usedThis = buffer;
// first check to see if we've been asked to hide all the subtracks
boolean hideKids = FALSE;
safef(buffer, sizeof buffer, "%s_hideKids", track->track);
s = cartOptionalString(cart, buffer);
if (s == NULL && startsWith("hub_", track->track))
s = cartOptionalString(cart, usedThis = trackHubSkipHubName(buffer));
if (s != NULL)
hideKids = TRUE;
cartRemove(cart, usedThis); // we don't want these _hideKids variables in the cart
// now see if we have any specified visibilities
struct track *subtrack;
for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next)
{
boolean undecoratedVis = FALSE;
char *s = hideTracks ? cgiOptionalString( subtrack->track) : cartOptionalString(cart, subtrack->track);
if (s == NULL && startsWith("hub_", subtrack->track))
{
undecoratedVis = TRUE;
s = hideTracks ? cgiOptionalString(trackHubSkipHubName(subtrack->track)) : cartOptionalString(cart, trackHubSkipHubName(subtrack->track));
}
safef(buffer, sizeof buffer, "%s_sel", subtrack->track);
if (s != NULL)
{
subtrack->visibility = hTvFromString(s);
cartSetString(cart, subtrack->track, s);
if (sameString("hide", s))
cartSetString(cart, buffer, "0");
else
cartSetString(cart, buffer, "1");
if (undecoratedVis)
cartRemove(cart, trackHubSkipHubName(subtrack->track)); // remove the undecorated version
}
else if (hideKids && isSubtrackVisible(subtrack))
cartSetString(cart, buffer, "0");
}
}
}
return trackList;
}
char *collapseGroupVar(char *name)
/* Construct cart variable name for collapsing group */
{
static char varName[256];
safef(varName, sizeof(varName),
"%s%s_%s_%s", "hgt", "group", name, "close");
return (cloneString(varName));
}
boolean isCollapsedGroup(struct group *grp)
/* Determine if group is collapsed */
{
return cartUsualInt(cart, collapseGroupVar(grp->name), grp->defaultIsClosed);
}
void collapseGroupGoodies(boolean isOpen, boolean wantSmallImage,
char **img, char **indicator, char **otherState)
/* Get image, char representation of image, and 'otherState' (1 or 0)
* for a group, based on whether it is collapsed, and whether we want
* larger or smaller image for collapse box */
{
if (otherState)
*otherState = (isOpen ? "1" : "0");
if (indicator)
*indicator = (isOpen ? "-" : "+");
if (img)
{
if (wantSmallImage)
*img = (isOpen ? "../images/remove_sm.gif" : "../images/add_sm.gif");
else
*img = (isOpen ? "../images/remove.gif" : "../images/add.gif");
}
}
void collapseGroup(char *name, boolean doCollapse)
/* Set cart variable to cause group to collapse */
{
cartSetBoolean(cart, collapseGroupVar(name), doCollapse);
}
void myControlGridStartCell(struct controlGrid *cg, boolean isOpen, char *id)
/* Start a new cell in control grid; support Javascript open/collapsing by including id's in tr's.
id is used as id prefix (a counter is added to make id's unique). */
{
static int counter = 1;
if (cg->columnIx == cg->columns)
controlGridEndRow(cg);
if (!cg->rowOpen)
{
#if 0
/* This is unnecessary, b/c we can just use a blank display attribute to show the element rather
than figuring out what the browser specific string is to turn on display of the tr;
however, we may want to put in browser specific strings in the future, so I'm leaving this
code in as a reference. */
char *ua = getenv("HTTP_USER_AGENT");
char *display = ua && stringIn("MSIE", ua) ? "block" : "table-row";
#endif
// use counter to ensure unique tr id's (prefix is used to find tr's in javascript).
printf("
");
}
static void pruneRedundantCartVis(struct track *trackList)
/* When the config page or track form has been submitted, there usually
* are many track visibility cart variables that have not been changed
* from the default. To keep down cart bloat, prune those out before we
* save the cart. changeTrackVis does this too, but this is for the
* more common case where track visibilities are tweaked. */
{
struct track *track;
for (track = trackList; track != NULL; track = track->next)
{
char *cartVis = cartOptionalString(cart, track->track);
if (cartVis != NULL && hTvFromString(cartVis) == track->tdb->visibility)
cartRemove(cart, track->track);
}
}
static int getMaxWindowToDraw(struct trackDb *tdb)
/* If trackDb setting maxWindowToDraw exists and is a sensible size, return it, else 0. */
{
if (tdb == NULL)
return 0;
char *maxWinToDraw = trackDbSettingClosestToHome(tdb, "maxWindowToDraw");
if (isNotEmpty(maxWinToDraw))
{
unsigned maxWTD = sqlUnsigned(maxWinToDraw);
if (maxWTD > 1)
return maxWTD;
}
return 0;
}
static void drawMaxWindowWarning(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg,
int xOff, int yOff, int width, MgFont *font, Color color,
enum trackVisibility vis)
/* This is a stub drawItems handler to be swapped in for the usual drawItems when the window
* size is larger than the threshold specified by trackDb setting maxWindowToDraw. */
{
int maxWinToDraw = getMaxWindowToDraw(tg->tdb);
char commafied[256];
sprintLongWithCommas(commafied, maxWinToDraw);
char message[512];
safef(message, sizeof(message), "zoom in to <= %s bases to view items", commafied);
Color yellow = hvGfxFindRgb(hvg, &undefinedYellowColor);
hvGfxBox(hvg, xOff, yOff, width, tg->heightPer, yellow);
hvGfxTextCentered(hvg, xOff, yOff, width, tg->heightPer, MG_BLACK, font, message);
}
static void checkMaxWindowToDraw(struct track *tg)
/* If (winEnd - winStart) > trackDb setting maxWindowToDraw, force track to a dense line
* that will ask the user to zoom in closer to see track items and return TRUE so caller
* can skip loading items. */
{
int maxWinToDraw = getMaxWindowToDraw(tg->tdb);
if (tdbIsComposite(tg->tdb))
{
struct track *subtrack;
for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next)
{
if (!isSubtrackVisible(subtrack))
continue;
maxWinToDraw = getMaxWindowToDraw(subtrack->tdb);
if (maxWinToDraw > 1 && (winEnd - winStart) > maxWinToDraw)
{
subtrack->loadItems = dontLoadItems;
subtrack->drawItems = drawMaxWindowWarning;
subtrack->limitedVis = tvDense;
subtrack->limitedVisSet = TRUE;
}
}
}
else if (maxWinToDraw > 1 && (winEnd - winStart) > maxWinToDraw)
{
tg->loadItems = dontLoadItems;
tg->drawItems = drawMaxWindowWarning;
tg->limitedVis = tvDense;
tg->limitedVisSet = TRUE;
}
}
void printTrackInitJavascript(struct track *trackList)
{
hPrintf("\n", hgtJsCommand, hgtJsCommand);
}
void jsCommandDispatch(char *command, struct track *trackList)
/* Dispatch a command sent to us from some javaScript event.
* This gets executed after the track list is built, but before
* the track->loadItems methods are called. */
{
if (startsWithWord("makeItems", command))
makeItemsJsCommand(command, trackList, trackHash);
else
warn("Unrecognized jsCommand %s", command);
}
void parentChildCartCleanup(struct track *trackList,struct cart *newCart,struct hash *oldVars)
/* When composite/view settings changes, remove subtrack specific vis
When superTrackChild is found and selected, shape superTrack to match. */
{
struct lm *lm = lmInit(0); /* Speed tweak cleanup with scatch memory pool. */
struct track *track = trackList;
for (;track != NULL; track = track->next)
{
boolean shapedByubtrackOverride = FALSE;
boolean cleanedByContainerSettings = FALSE;
// Top-down 'cleanup' MUST GO BEFORE bottom up reshaping.
cleanedByContainerSettings = cartTdbTreeCleanupOverrides(track->tdb,newCart,oldVars, lm);
if (tdbIsContainer(track->tdb))
{
shapedByubtrackOverride = cartTdbTreeReshapeIfNeeded(cart,track->tdb);
if (shapedByubtrackOverride)
track->visibility = tdbVisLimitedByAncestors(cart,track->tdb,TRUE,TRUE);
}
if ((shapedByubtrackOverride || cleanedByContainerSettings)
&& tdbIsSuperTrackChild(track->tdb)) // Either cleanup may require supertrack intervention
{ // Need to update track visibility
// Unfortunately, since supertracks are not in trackList, this occurs on superChildren,
// So now we need to find the supertrack and take changed cart values of its children
struct slRef *childRef;
for (childRef = track->tdb->parent->children;childRef != NULL;childRef = childRef->next)
{
struct trackDb * childTdb = childRef->val;
struct track *child = hashFindVal(trackHash, childTdb->track);
if (child != NULL && child->track!=NULL)
{
char *cartVis = cartOptionalString(cart,child->track);
if (cartVis)
child->visibility = hTvFromString(cartVis);
}
}
}
}
lmCleanup(&lm);
}
struct paraFetchData
{
struct paraFetchData *next;
struct track *track;
boolean done;
};
static boolean isTrackForParallelLoad(struct track *track)
/* Is this a track that should be loaded in parallel ? */
{
char *bdu = trackDbSetting(track->tdb, "bigDataUrl");
return (startsWithWord("bigWig" , track->tdb->type)
|| startsWithWord("mathWig" , track->tdb->type)
|| startsWithWord("bigBed" , track->tdb->type)
|| startsWithWord("bigPsl" , track->tdb->type)
|| startsWithWord("bigNarrowPeak" , track->tdb->type)
|| startsWithWord("bigGenePred" , track->tdb->type)
|| startsWithWord("bigChain" , track->tdb->type)
|| startsWithWord("bam" , track->tdb->type)
|| startsWithWord("halSnake", track->tdb->type)
|| startsWithWord("bigLolly", track->tdb->type)
|| startsWithWord("vcfTabix", track->tdb->type))
// XX code-review: shouldn't we error abort if the URL is not valid?
&& (bdu && isValidBigDataUrl(bdu, FALSE))
&& !(containsStringNoCase(bdu, "dl.dropboxusercontent.com"))
&& (track->subtracks == NULL);
}
static void findLeavesForParallelLoad(struct track *trackList, struct paraFetchData **ppfdList)
/* Find leaves of track tree that are remote network resources for parallel-fetch loading */
{
struct track *track;
if (!trackList)
return;
for (track = trackList; track != NULL; track = track->next)
{
if (track->visibility != tvHide)
{
if (isTrackForParallelLoad(track))
{
struct paraFetchData *pfd;
AllocVar(pfd);
pfd->track = track; // need pointer to be stable
slAddHead(ppfdList, pfd);
track->parallelLoading = TRUE;
}
struct track *subtrack;
for (subtrack=track->subtracks; subtrack; subtrack=subtrack->next)
{
if (isTrackForParallelLoad(subtrack))
{
if (tdbVisLimitedByAncestors(cart,subtrack->tdb,TRUE,TRUE) != tvHide)
{
struct paraFetchData *pfd;
AllocVar(pfd);
pfd->track = subtrack; // need pointer to be stable
slAddHead(ppfdList, pfd);
subtrack->parallelLoading = TRUE;
}
}
}
}
}
}
static pthread_mutex_t pfdMutex = PTHREAD_MUTEX_INITIALIZER;
static struct paraFetchData *pfdList = NULL, *pfdRunning = NULL, *pfdDone = NULL, *pfdNeverStarted = NULL;
static void *remoteParallelLoad(void *threadParam)
/* Each thread loads tracks in parallel until all work is done. */
{
pthread_t *pthread = threadParam;
struct paraFetchData *pfd = NULL;
pthread_detach(*pthread); // this thread will never join back with it's progenitor
// Canceled threads that might leave locks behind,
// so the theads are detached and will be neither joined nor canceled.
boolean allDone = FALSE;
while(1)
{
pthread_mutex_lock( &pfdMutex );
if (!pfdList)
{
allDone = TRUE;
}
else
{ // move it from the waiting queue to the running queue
pfd = slPopHead(&pfdList);
slAddHead(&pfdRunning, pfd);
}
pthread_mutex_unlock( &pfdMutex );
if (allDone)
return NULL;
long thisTime = 0, lastTime = 0;
if (measureTiming)
lastTime = clock1000();
/* protect against errAbort */
struct errCatch *errCatch = errCatchNew();
if (errCatchStart(errCatch))
{
pfd->done = FALSE;
checkMaxWindowToDraw(pfd->track);
pfd->track->loadItems(pfd->track);
pfd->done = TRUE;
}
errCatchEnd(errCatch);
if (errCatch->gotError)
{
pfd->track->networkErrMsg = cloneString(errCatch->message->string);
pfd->done = TRUE;
}
errCatchFree(&errCatch);
if (measureTiming)
{
thisTime = clock1000();
pfd->track->loadTime = thisTime - lastTime;
}
pthread_mutex_lock( &pfdMutex );
slRemoveEl(&pfdRunning, pfd); // this list will not be huge
slAddHead(&pfdDone, pfd);
pthread_mutex_unlock( &pfdMutex );
}
}
static int remoteParallelLoadWait(int maxTimeInSeconds)
/* Wait, checking to see if finished (completed or errAborted).
* If timed-out or never-ran, record error status.
* Return error count. */
{
int maxTimeInMilliseconds = 1000 * maxTimeInSeconds;
struct paraFetchData *pfd;
int errCount = 0;
int waitTime = 0;
while(1)
{
sleep1000(50); // milliseconds
waitTime += 50;
boolean done = TRUE;
pthread_mutex_lock( &pfdMutex );
if (pfdList || pfdRunning)
done = FALSE;
pthread_mutex_unlock( &pfdMutex );
if (done)
break;
if (waitTime >= maxTimeInMilliseconds)
break;
}
pthread_mutex_lock( &pfdMutex );
pfdNeverStarted = pfdList;
pfdList = NULL; // stop the workers from starting any more waiting track loads
for (pfd = pfdNeverStarted; pfd; pfd = pfd->next)
{
// track was never even started
char temp[256];
safef(temp, sizeof temp, "Ran out of time (%d milliseconds) unable to process %s", maxTimeInMilliseconds, pfd->track->track);
pfd->track->networkErrMsg = cloneString(temp);
++errCount;
}
for (pfd = pfdRunning; pfd; pfd = pfd->next)
{
// unfinished track
char temp[256];
safef(temp, sizeof temp, "Timeout %d milliseconds exceeded processing %s", maxTimeInMilliseconds, pfd->track->track);
pfd->track->networkErrMsg = cloneString(temp);
++errCount;
}
for (pfd = pfdDone; pfd; pfd = pfd->next)
{
// some done tracks may have errors
if (pfd->track->networkErrMsg)
++errCount;
}
pthread_mutex_unlock( &pfdMutex );
return errCount;
}
static int avgLoadTime(struct track *track)
/* calculate average loadtime across all windows */
{
int totalLoadTime = 0;
int winCount = 0;
while(track)
{
++winCount;
totalLoadTime += track->loadTime;
track = track->nextWindow;
}
return (((float)totalLoadTime / winCount) + 0.5);
}
static int avgDrawTime(struct track *track)
/* calculate average drawtime across all windows */
{
int totalDrawTime = 0;
int winCount = 0;
while(track)
{
++winCount;
totalDrawTime += track->drawTime;
track = track->nextWindow;
}
return (((float)totalDrawTime / winCount) + 0.5);
}
static int avgWigMafLoadTime(struct track *track)
/* calculate average wigMaf loadtime across all windows */
{
int totalLoadTime = 0;
int winCount = 0;
while(track)
{
++winCount;
if (startsWith("wigMaf", track->tdb->type))
if (track->subtracks)
if (track->subtracks->loadTime)
totalLoadTime += track->subtracks->loadTime;
track = track->nextWindow;
}
return (((float)totalLoadTime / winCount) + 0.5);
}
static void printTrackTiming()
{
hPrintf("track, load time, draw time, total (first window) \n");
if (virtMode)
hPrintf("average for all windows in red \n");
struct track *track;
for (track = trackList; track != NULL; track = track->next)
{
if (track->visibility == tvHide)
continue;
if (trackIsCompositeWithSubtracks(track)) //TODO: Change when tracks->subtracks are always set for composite
{
struct track *subtrack;
for (subtrack = track->subtracks; subtrack != NULL;
subtrack = subtrack->next)
if (isSubtrackVisible(subtrack))
{
hPrintf("%s, %d, %d, %d \n", subtrack->shortLabel,
subtrack->loadTime, subtrack->drawTime,
subtrack->loadTime + subtrack->drawTime);
if (virtMode)
{
int avgLoad = avgLoadTime(subtrack);
int avgDraw = avgDrawTime(subtrack);
hPrintf("%s, %d, %d, %d \n", subtrack->shortLabel,
avgLoad, avgDraw,
avgLoad + avgDraw);
}
}
}
else
{
hPrintf("%s, %d, %d, %d \n",
track->shortLabel, track->loadTime, track->drawTime,
track->loadTime + track->drawTime);
if (startsWith("wigMaf", track->tdb->type))
if (track->subtracks)
if (track->subtracks->loadTime)
hPrintf(" %s wiggle, load %d \n",
track->shortLabel, track->subtracks->loadTime);
}
if (virtMode)
{
int avgLoad = avgLoadTime(track);
int avgDraw = avgDrawTime(track);
hPrintf("%s, %d, %d, %d \n", track->shortLabel,
avgLoad, avgDraw,
avgLoad + avgDraw);
int avgWigMafLoad = avgWigMafLoadTime(track);
if (avgWigMafLoad > 0)
{
hPrintf(" %s wiggle, load %d \n",
track->shortLabel, avgWigMafLoad);
}
}
}
hPrintf("\n");
}
void initTrackList()
/* need to init tracklist, sometimes early */
{
if (!trackList)
{
if (measureTiming)
measureTime("Time before getTrackList");
boolean defaultTracks = cgiVarExists("hgt.reset");
trackList = getTrackList(&groupList, defaultTracks ? -1 : -2);
if (measureTiming)
measureTime("Time after visibilities");
makeGlobalTrackHash(trackList);
}
}
struct track *getTrackListForOneTrack(char *trackName)
/* Fetch trackList for a single trackName using hgt.trackNameFilter. */
{
struct track *saveTrackList = trackList;
struct group *saveGroupList = groupList;
char *saveTrackNameFilter = cloneString(cartOptionalString(cart, "hgt.trackNameFilter"));
// This is an attempt to both get around the limitation imposed by ajax callback hgt.trackNameFilter,
// and also to try to optimize it a little so that callbacks only have to load a trackList containing
// only the emGeneTable.
cartSetString(cart, "hgt.trackNameFilter", trackName);
initTrackList(); // initialize trackList early if needed
struct track *returnTrackList = trackList;
cartRemove(cart, "hgt.trackNameFilter");
// restore
if (saveTrackNameFilter)
{
cartSetString(cart, "hgt.trackNameFilter", saveTrackNameFilter);
}
trackList = saveTrackList;
groupList = saveGroupList;
return returnTrackList;
}
void doNextPrevItem(boolean goNext, char *trackName)
/* In case a next item arrow was clicked on a track, change */
/* position (i.e. winStart, winEnd, etc.) based on what track it was */
{
// create new trackList with just trackName
struct track *myTrackList = getTrackListForOneTrack(trackName);
struct track *track = trackFindByName(myTrackList, trackName);
if ((track != NULL) && (track->nextPrevItem != NULL))
{
// custom track big* tracks have pre-opened handle which we should not use
// because that same bbiFile will get used later in the full track list
track->bbiFile = NULL;
track->nextPrevItem(track, goNext);
}
}
void findBestEMGeneTable(struct track *myTrackList)
/* Find the best gene table to use for exonMostly and geneMostly. */
{
// TODO add support for choosing any gene table of type genePred, genePreExt, bigGenePred
// TODO add support for assembly hubs
if (trackHubDatabase(database)) // assembly hub? not supported yet
return; // any table-name matches might just be coincidence in an assembly hub
// although the hub_ prefix on the track name would help prevent name collisions.
char *orderedTables[] =
{"knownGene", "refGene", "ensGene",
"flybaseGene", "sangerGene", "augustusGene", "genscan"};
int i, len;
for(i=0, len=ArraySize(orderedTables); i next; window; window=window->next)
{
if (!sameString(window->chromName,windows->chromName))
return TRUE;
}
return FALSE;
}
static void setSharedLimitedVisAcrossWindows(struct track *track)
/* Look for lowest limitedVis across all windows
* if found, set all windows to same lowest limited vis. */
{
enum trackVisibility sharedVis = 99;
struct track *tg;
for (tg=track; tg; tg=tg->nextWindow)
{
if (tg->limitedVisSet)
{
if (tg->limitedVis < sharedVis)
sharedVis = tg->limitedVis;
}
}
if (sharedVis != 99)
{
for (tg=track; tg; tg=tg->nextWindow)
{
tg->limitedVis = sharedVis;
tg->limitedVisSet = TRUE;
}
}
}
static void setSharedErrorsAcrossWindows(struct track *track)
/* Look for network errors across all windows
* if found, set all windows to same errMsg and set bigWarn track handlers. */
{
char *sharedErrMsg = NULL;
struct track *tg;
for (tg=track; tg; tg=tg->nextWindow)
{
if (!sharedErrMsg && tg->networkErrMsg)
{
sharedErrMsg = tg->networkErrMsg;
break;
}
}
if (sharedErrMsg)
{
for (tg=track; tg; tg=tg->nextWindow)
{
tg->networkErrMsg = sharedErrMsg;
tg->drawItems = bigDrawWarning;
tg->totalHeight = bigWarnTotalHeight;
}
}
}
void outCollectionsToJson()
/* Output the current collections to the hgTracks JSON block. */
{
struct grp *groupList = NULL;
char buffer[4096];
safef(buffer, sizeof buffer, "%s-%s", customCompositeCartName, database);
char *hubFile = cartOptionalString(cart, buffer);
if (hubFile != NULL)
{
char *hubName = hubNameFromUrl(hubFile);
struct trackDb *hubTdbs = hubCollectTracks( database, &groupList);
struct trackDb *tdb;
struct jsonElement *jsonList = NULL;
for(tdb = hubTdbs; tdb; tdb = tdb->next)
{
if (sameString(tdb->grp, hubName))
{
if (jsonList == NULL)
jsonList = newJsonList(NULL);
struct jsonElement *collection = newJsonObject(newHash(4));
jsonObjectAdd(collection, "track", newJsonString(tdb->track));
jsonObjectAdd(collection, "shortLabel", newJsonString(tdb->shortLabel));
jsonListAdd(jsonList, collection);
}
}
if (jsonList != NULL)
jsonObjectAdd(jsonForClient, "collections", jsonList);
}
}
void doTrackForm(char *psOutput, struct tempName *ideoTn)
/* Make the tracks display form with the zoom/scroll buttons and the active
* image. If the ideoTn parameter is not NULL, it is filled in if the
* ideogram is created. */
{
struct group *group;
struct track *track;
char *freezeName = NULL;
boolean hideAll = cgiVarExists("hgt.hideAll");
boolean defaultTracks = cgiVarExists("hgt.reset");
boolean showedRuler = FALSE;
boolean showTrackControls = cartUsualBoolean(cart, "trackControlsOnMain", TRUE);
long thisTime = 0, lastTime = 0;
basesPerPixel = ((float)virtWinBaseCount) / ((float)fullInsideWidth);
zoomedToBaseLevel = (virtWinBaseCount <= fullInsideWidth / tl.mWidth);
zoomedToCodonLevel = (ceil(virtWinBaseCount/3) * tl.mWidth) <= fullInsideWidth;
zoomedToCdsColorLevel = (virtWinBaseCount <= fullInsideWidth*3);
if (psOutput != NULL)
{
hPrintDisable();
hideControls = TRUE;
withNextItemArrows = FALSE;
withNextExonArrows = FALSE;
hgFindMatches = NULL;
}
/* Tell browser where to go when they click on image. */
hPrintf("