src/tracks/tracks.c 1.9
1.9 2009/08/19 23:34:06 angie
Added option to mgSaveToGif and its call stack, to use GIF's Graphic Control Extension to make memgfx's background color (0) transparent. Also corrected terminology for PNG in .h files: useAlpha -> useTransparency.
Index: src/tracks/tracks.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/tracks/tracks.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -b -B -U 1000000 -r1.8 -r1.9
--- src/tracks/tracks.c 7 Nov 2006 20:03:31 -0000 1.8
+++ src/tracks/tracks.c 19 Aug 2009 23:34:06 -0000 1.9
@@ -1,2008 +1,2008 @@
/* Tracks - this program displays multiple tracks of info associated with
* a piece of DNA. */
#include "common.h"
#include "memalloc.h"
#include "portable.h"
#include "dnautil.h"
#include "dnaseq.h"
#include "nt4.h"
#include "gdf.h"
#include "fuzzyFind.h"
#include "wormdna.h"
#include "snof.h"
#include "memgfx.h"
#include "cda.h"
#include "xa.h"
#include "cheapcgi.h"
#include "htmshell.h"
#define COLOR_TRACKS /* undef this for black and white version. */
static char titleBuf[80];
char **chromNames;
int chromCount;
char *organism = "celegans";
struct trackInput
/* Input that goes from one incarnation of program to another goes here. */
{
char *title;
char *hilite;
char *dynFa;
char *dynCda;
char *whereHid;
int mouseX, mouseY;
char *locus;
char *chrom;
int chromStart, chromEnd;
char strand;
int relStart;
boolean withCosmids, withGenes, withGenie, withCdna, withEmbryoCdna,
withLeftLabels, withCenterLabels, withBriggsae;
};
static struct trackInput ti;
#define mapSig 0x9f7e3578
enum mapType
/* There's one of these for each type of area in the graphic display
* that the user can click on. */
{
mtEnd,
mtDoNothing,
mtDynTitle,
mtDynTrack,
mtDynCondensed,
mtDynLabel,
mtCosmidTitle,
mtCosmidTrack,
mtCosmidCondensed,
mtCosmidLabel,
mtGeneTitle,
mtGeneTrack,
mtGeneOnBox,
mtGeneCondensed,
mtGeneLabel,
mtCdnaTitle,
mtCdnaTrack,
mtCdnaCondensed,
mtCdnaLabel,
mtBriggsaeTitle,
mtBriggsaeTrack,
mtBriggsaeOnBox,
mtBriggsaeCondensed,
mtBriggsaeLabel,
mtWiseTitle,
mtWiseTrack,
mtWiseCondensed,
mtWiseLabel,
mtRuler,
mtOutside, /* Outside any feature. */
};
void mapReadString(FILE *f, char **ps)
/* Read string from map file. */
{
short len;
char *s;
mustReadOne(f, len);
if (len == 0)
s = NULL;
else
{
s = needMem(len+1);
mustRead(f, s, len);
}
*ps = s;
}
void mapWriteString(FILE *f, char *s)
/* Write string to map file. */
{
short len;
if (s == NULL)
{
len = 0;
writeOne(f, len);
}
else
{
len = strlen(s);
writeOne(f, len);
mustWrite(f, s, len);
}
}
void mapWriteBox(FILE *f, enum mapType mt, int x, int y, int width, int height, char *name, char strand)
/* Write out one hit box. */
{
writeOne(f, mt);
writeOne(f, x);
writeOne(f, y);
writeOne(f, width);
writeOne(f, height);
mapWriteString(f, name);
writeOne(f, strand);
}
void mapReadBox(FILE *f, enum mapType *mt, int *x, int *y, int *width, int *height, char **name, char *strand)
/* Read in one hit box. */
{
mustReadOne(f, *mt);
mustReadOne(f, *x);
mustReadOne(f, *y);
mustReadOne(f, *width);
mustReadOne(f, *height);
mapReadString(f, name);
mustReadOne(f, *strand);
}
boolean mapScanForHit(FILE *f, int mouseX, int mouseY,
enum mapType *mt, int *x, int *y, int *width, int *height, char **name, char *strand)
/* Scan file for one that hits mouse. Return TRUE if get one. */
{
for (;;)
{
mapReadBox(f, mt, x, y, width, height, name, strand);
if (*mt == mtEnd)
return FALSE;
if (mouseX >= *x && mouseX < *x + *width
&& mouseY >= *y && mouseY < *y + *height)
{
return TRUE;
}
freez(name);
}
}
void mapWriteHead(FILE *f, char *title, bits32 width, bits32 height,
char *where, char *chrom, int chromStart, int chromEnd, char strand,
char *hilitRange, int relStart)
{
bits32 sig = mapSig;
writeOne(f, sig);
mapWriteString(f, title);
writeOne(f,width);
writeOne(f,height);
mapWriteString(f, where);
mapWriteString(f, chrom);
writeOne(f, chromStart);
writeOne(f, chromEnd);
writeOne(f, strand);
mapWriteString(f, hilitRange);
writeOne(f, relStart);
}
void mapReadHead(FILE *f, char **title, bits32 *width, bits32 *height,
char **where, char **chrom, int *chromStart, int *chromEnd, char *strand,
char **hilitRange, int *relStart)
{
bits32 sig;
char *chromName;
mustReadOne(f, sig);
if (sig != mapSig)
errAbort("Bad map file");
mapReadString(f, title);
mustReadOne(f,*width);
mustReadOne(f,*height);
mapReadString(f, where);
mapReadString(f, &chromName);
*chrom = wormOfficialChromName(chromName);
freeMem(chromName);
mustReadOne(f, *chromStart);
mustReadOne(f, *chromEnd);
mustReadOne(f, *strand);
mapReadString(f, hilitRange);
mustReadOne(f, *relStart);
}
char dominantStrand(struct wormFeature *geneList)
/* Figure out most commonly used strand in list. */
{
int plusCount = 0;
int minusCount = 0;
struct wormFeature *gene;
for (gene = geneList; gene != NULL; gene = gene->next)
{
if (gene->typeByte == '+')
++plusCount;
else if (gene->typeByte == '-')
++minusCount;
}
if (plusCount > minusCount)
return '+';
else if (minusCount > plusCount)
return '-';
else
return '.';
}
void strandFixupPair(int start, int end, int *retStart, int *retEnd)
/* Fixup position of start and end if necessary if on - strand. */
{
if (ti.strand == '-')
{
int dnaSize = ti.chromEnd - ti.chromStart;
*retStart = reverseOffset(end - ti.chromStart, dnaSize) + 1 + ti.chromStart;
*retEnd = reverseOffset(start - ti.chromStart, dnaSize) + 1 + ti.chromStart;
}
else
{
*retStart = start;
*retEnd = end;
}
}
void fixOffsets(struct gdfGene *gene, long dnaStart, long dnaEnd,
char dnaStrand, long geneStart, long geneEnd)
/* Gdf offsets come in (aargh) offsets into the DNA in a bite sized
* gff file. Here we first convert them to chromosome offsets, and
* then into our local DNA offsets. */
{
long gffStart, gffEnd;
gdfGeneExtents(gene, &gffStart, &gffEnd);
gdfOffsetGene(gene, -gffStart + geneStart - dnaStart);
if (dnaStrand == '-')
gdfRcGene(gene, dnaEnd - dnaStart);
}
void drawHilite(struct memGfx *mg, int xOff, int yOff, int width, int height,
char *hiString)
/* Display hilite areas. */
{
int x1, x2, w;
int start, end;
char *s = cloneString(hiString);
char *words[256];
int wordCount;
char *parts[3];
int partCount;
int hiStart, hiEnd;
int baseWidth = ti.chromEnd - ti.chromStart;
int i;
mgSetClip(mg, xOff, yOff, width, height);
wordCount = chopString(s, ",", words, ArraySize(words));
for (i=0; i<wordCount; ++i)
{
partCount = chopString(words[i], " \t-", parts, ArraySize(parts));
if (partCount != 2 || !isdigit(parts[0][0]) || !isdigit(parts[1][0]))
errAbort("Badly formatted hilite string %s", hiString);
hiStart = atoi(parts[0]);
hiEnd = atoi(parts[1]);
strandFixupPair(hiStart, hiEnd, &start, &end);
x1 = roundingScale(start-ti.chromStart, width, baseWidth) + xOff;
x2 = roundingScale(end-ti.chromStart, width, baseWidth) + xOff;
w = x2-x1;
if (w < 1) w = 1;
mgDrawBox(mg, x1, yOff, w, height, MG_CYAN);
}
mgUnclip(mg);
freeMem(s);
}
int writeCenterLabel(boolean compactDisplay, FILE *mapFile, struct memGfx *mg, int x, int y, int width, int height,
MgFont *font, Color color, char *label, enum mapType mt)
/* Write a centered label. */
{
char text[80];
if (ti.withCenterLabels)
{
sprintf(text, "%s%s",
(compactDisplay ? "Condensed " : ""), label);
mgTextCentered(mg, x, y+1, width, height-1, color, font, text);
mapWriteBox(mapFile, mt, x, y, width, height, "", '.');
y += height;
}
return y;
}
struct wormFeature *stripNamelessClusters(struct wormFeature *oldList)
/* Remove nameless clusters from features list. */
{
struct wormFeature *newList = NULL, *el, *next;
el = oldList;
for (;;)
{
if (el == NULL)
break;
next = el->next;
if (wormIsNamelessCluster(el->name))
freeMem(el);
else
slAddHead(&newList, el);
el = next;
}
slReverse(&newList);
return newList;
}
char *abbreviateToFit(char *longString, MgFont *font, int width)
/* Return a string abbreviated enough to fit into space. */
{
int textWidth;
int charWidth;
static char abrv[33];
int charCount;
static int chopSize[] = {32, 16, 8, 6, 5, 4, 3, 2, 1};
int cs;
int i;
/* If string is empty or width is too small for one character return null. */
if (longString == NULL || longString[0] == 0)
return NULL;
charWidth = mgFontWidth(font, longString, 1);
if (charWidth > width)
return NULL;
/* If have enough space, return original unabbreviated string. */
textWidth = mgFontStringWidth(font, longString);
if (textWidth <= width)
return longString;
/* Ok, chop it down into abrv */
charCount = strlen(longString);
for (i=0; i<ArraySize(chopSize); ++i)
{
cs = chopSize[i];
if (cs > charCount)
continue;
textWidth = mgFontWidth(font, longString, cs);
if (textWidth <= width)
{
strncpy(abrv, longString, cs);
return abrv;
}
}
assert(FALSE);
return NULL;
}
struct trackGroup
/* Structure that displays a group of tracks. */
{
struct trackGroup *next; /* Next on list. */
char *checkBoxName; /* Name of cgiBoolean that turns us on. */
char *longLabel; /* Long label to put in center. */
char *shortLabel; /* Short label to put on side. */
enum mapType labelType; /* What to do when they click on left label. */
enum mapType trackType; /* What to do when they click on expanded track. */
enum mapType condensedType;/* What to do when they click on condensed track. */
enum mapType titleType; /* What to do when they click on center label. */
struct rgbColor color; /* Main color. */
struct rgbColor grayColor; /* Dimmer color. */
Color ixColor; /* Indexed color (calculated by drawTracks) */
Color ixGrayColor; /* Indexed dim color (calculated by drawTracks) */
void (*loadItems)(struct trackGroup *tg, boolean isCondensed, char *chrom, int start, int end);
/* loadItems loads up items for the chromosome range indicated. It also usually sets the
* following variables. */
void *items; /* Some type of slList of items. */
char *(*itemName)(struct trackGroup *tg, void *item);
/* Return name of one of a member of items above. */
int (*totalHeight)(struct trackGroup *tg, MgFont *font);
/* Return total height. Called before and afer drawItems.
* Must set the following variables. */
int height; /* Total height - must be set by above call. */
int lineHeight; /* Height per track including border. */
int heightPer; /* Height per track minus border. */
void (*drawItems)(struct trackGroup *tg, FILE *mapFile, int chromStart, int chromEnd,
struct memGfx *mg, int xOff, int yOff, int width,
MgFont *font, Color color, Color grayColor);
/* Draw item list, one per track. */
void (*drawCondensedItems)(struct trackGroup *tg, FILE *mapFile, int chromStart, int chromEnd,
struct memGfx *mg, int xOff, int yOff, int width, int height,
MgFont *font, Color color, Color grayColor);
/* Draw item list all condensed onto one track. */
void (*freeItems)(struct trackGroup *tg, boolean isCondensed);
/* Free item list. */
void *customPt; /* Misc pointer variable unique to group. */
int customInt; /* Misc int variable unique to group. */
int subType; /* Variable to say what subtype this is for similar groups
* to share code. */
};
static int tgUsualHeight(struct trackGroup *tg, MgFont *font)
/* Most track groups will use this to figure out the height they use. */
{
tg->lineHeight = mgFontLineHeight(font);
tg->heightPer = tg->lineHeight - 1;
tg->height = slCount(tg->items) * tg->lineHeight;
return tg->height;
}
static void tgDrawFeatures(struct trackGroup *tg, FILE *mapFile, int chromStart, int chromEnd,
struct memGfx *mg, int xOff, int yOff, int width,
MgFont *font, Color color, Color grayColor)
/* Draw a simple features list. */
{
int x1,x2,w;
int baseWidth = chromEnd - chromStart;
double scaleFactor = (double)width/baseWidth;
struct wormFeature *feat;
char *name;
int y = yOff;
for (feat = tg->items; feat != NULL; feat = feat->next)
{
int start, end;
strandFixupPair(feat->start, feat->end, &start, &end);
x1 = (int)(scaleFactor * (start-chromStart)) + xOff;
x2 = (int)(scaleFactor * (end-chromStart)) + xOff;
/* Clip so that centered text tends to be in display. */
if (x1 < xOff)
x1 = xOff;
if (x2 > xOff + width)
x2 = xOff + width;
w = x2-x1;
mgDrawBox(mg, x1, y, w, tg->heightPer, color);
if ((name = abbreviateToFit(feat->name, font, w)) != NULL)
{
mgTextCentered(mg, x1, y+1, w, tg->heightPer, MG_WHITE, font, feat->name);
}
mapWriteBox(mapFile, tg->trackType, xOff, y, width, tg->lineHeight, feat->name, '+');
y += tg->lineHeight;
}
}
char *tgFeatureName(struct trackGroup *tg, void *item)
/* Return name of feature. */
{
struct wormFeature *feat = item;
return feat->name;
}
static void tgDrawCondensedFeatures(struct trackGroup *tg, FILE *mapFile, int chromStart, int chromEnd,
struct memGfx *mg, int xOff, int yOff, int width, int height,
MgFont *font, Color color, Color grayColor)
/* Draw a simple features list in condensed format. */
{
int x1,x2,w;
struct wormFeature *feat;
int baseWidth = chromEnd - chromStart;
double scaleFactor = (double)width/baseWidth;
for (feat = tg->items; feat != NULL; feat = feat->next)
{
int start, end;
strandFixupPair(feat->start, feat->end, &start, &end);
x1 = (int)(scaleFactor * (start-chromStart));
x2 = (int)(scaleFactor * (end-chromStart));
w = x2-x1;
if (w < 1) w = 1;
mgDrawBox(mg, x1+xOff, yOff, w, height, color);
}
mapWriteBox(mapFile, tg->condensedType, xOff, yOff, width, height, "", '+');
}
static void tgFreeFeatures(struct trackGroup *tg, boolean isCondensed)
/* Free simple fireatures list. */
{
slFreeList(&tg->items);
}
void tgSetDefaults(struct trackGroup *tg)
/* Set up reasonable values for trackGroup object. */
{
zeroBytes(tg, sizeof(*tg));
tg->checkBoxName = "";
tg->longLabel = "";
tg->shortLabel = "";
tg->labelType = mtDoNothing;
tg->trackType = mtDoNothing;
tg->condensedType = mtDoNothing;
tg->titleType = mtDoNothing;
tg->color.r = tg->color.g = tg->color.b = 0;
tg->grayColor.r = tg->grayColor.g = tg->grayColor.b = 140;
tg->loadItems = NULL;
tg->itemName = tgFeatureName;
tg->totalHeight = tgUsualHeight;
tg->drawItems = tgDrawFeatures;
tg->drawCondensedItems = tgDrawCondensedFeatures;
tg->freeItems = tgFreeFeatures;
};
void cosmidLoadItems(struct trackGroup *tg, boolean isCondensed, char *chrom, int start, int end)
{
tg->items = wormCosmidsInRange(chrom, start, end);
}
struct trackGroup *getCosmidTg()
/* Get trackGroup for cosmids. */
{
static struct trackGroup tg;
tgSetDefaults(&tg);
tg.checkBoxName = "cosmids";
tg.longLabel = "Cosmids";
tg.shortLabel = "Cosmids";
tg.labelType = mtCosmidLabel;
tg.trackType = mtCosmidTrack;
tg.condensedType = mtCosmidCondensed;
tg.titleType = mtCosmidTitle;
#ifdef COLOR_TRACKS
tg.color.r = 180;
tg.color.g = 0;
tg.color.b = 0;
#else
tg.grayColor.r = 140;
tg.grayColor.g = 140;
tg.grayColor.b = 140;
#endif /* COLOR_TRACKS */
tg.loadItems = cosmidLoadItems;
return &tg;
}
char *tgCdnaName(struct trackGroup *tg, void *item)
/* Return name of feature. */
{
struct cdaAli *cda = item;
return cda->name;
}
void cdnaLoadItems(struct trackGroup *tg, boolean isCondensed, char *chrom, int start, int end)
/* Get all cdna items - both mixed and embryo. */
{
if (isCondensed)
tg->items = wormCdnasInRange(ti.chrom, ti.chromStart, ti.chromEnd);
else
{
struct cdaAli *aliList;
aliList = wormCdaAlisInRange(ti.chrom, ti.chromStart, ti.chromEnd);
cdaCoalesceBlocks(aliList);
tg->items = aliList;
}
}
static void cdnaDrawItems(struct trackGroup *tg, FILE *mapFile, int chromStart, int chromEnd,
struct memGfx *mg, int xOff, int yOff, int width,
MgFont *font, Color color, Color grayColor)
/* Draw cDNA aligment items. */
{
struct cdaAli *cda;
char dirChar;
if (ti.strand == '-')
cdaRcList(tg->items, chromStart, chromEnd-chromStart);
for (cda = tg->items; cda != NULL; cda = cda->next)
{
dirChar = (tg->subType ? 0 : cdaDirChar(cda, ti.strand));
/* Show aligments. */
cdaShowAlignmentTrack(mg, xOff, yOff, width, tg->heightPer, color, grayColor,
chromEnd-chromStart, chromStart, cda, dirChar);
/* Define area for image map. */
mapWriteBox(mapFile, tg->trackType, xOff, yOff, width, tg->lineHeight, cda->name, '.');
yOff += tg->lineHeight;
}
}
static void cdnaFreeItems(struct trackGroup *tg, boolean isCondensed)
/* Free up items of cdna. */
{
if (isCondensed)
slFreeList(&tg->items);
else
cdaFreeAliList((struct cdaAli**)(&tg->items));
}
struct trackGroup *getCdnaTg()
/* Get trackGroup for cDNA (mixed). */
{
static struct trackGroup tg;
static boolean initted = FALSE;
if (!initted)
{
initted = TRUE;
tgSetDefaults(&tg);
tg.checkBoxName = "cDNA";
tg.longLabel = "cDNA Alignments";
tg.shortLabel = "cDNA/ESTs";
tg.labelType = mtCdnaLabel;
tg.trackType = mtCdnaTrack;
tg.condensedType = mtCdnaCondensed;
tg.titleType = mtCdnaTitle;
tg.loadItems = cdnaLoadItems;
tg.itemName = tgCdnaName;
tg.drawItems = cdnaDrawItems;
tg.freeItems = cdnaFreeItems;
}
return &tg;
}
void separateEmbryonicAli(struct cdaAli *inCda, struct cdaAli **retMixedCda, struct cdaAli **retEmbCda)
/* Separate embryonic and non-embryonic alignments */
{
struct cdaAli *mixedCda = NULL, *embCda = NULL, *cda, *next;
for (cda = inCda; cda != NULL; )
{
next = cda->next;
if (cda->isEmbryonic)
{
slAddHead(&embCda, cda);
}
else
{
slAddHead(&mixedCda, cda);
}
cda = next;
}
slReverse(&embCda);
slReverse(&mixedCda);
*retEmbCda = embCda;
*retMixedCda = mixedCda;
}
void separateEmbryonicFeat(struct wormFeature *inFeat, struct wormFeature **retMixedFeat, struct wormFeature **retEmbFeat)
/* Separate embryonic and non-embryonic features */
{
struct wormFeature *mixedFeat = NULL, *embFeat = NULL, *feat, *next;
for (feat = inFeat; feat != NULL; )
{
next = feat->next;
if (feat->typeByte)
{
slAddHead(&embFeat, feat);
}
else
{
slAddHead(&mixedFeat, feat);
}
feat = next;
}
slReverse(&embFeat);
slReverse(&mixedFeat);
*retEmbFeat = embFeat;
*retMixedFeat = mixedFeat;
}
void embryonicCdnaLoadItems(struct trackGroup *tg, boolean isCondensed, char *chrom, int start, int end)
/* Get all cdna items - both mixed and embryo. */
/* This actually steals them from cdnaTrackGroup. */
{
struct trackGroup *cdnaTg = getCdnaTg();
if (cdnaTg->items == NULL)
cdnaTg->loadItems(cdnaTg, isCondensed, chrom, start, end);
if (isCondensed)
{
separateEmbryonicFeat(cdnaTg->items, (struct wormFeature**)(&cdnaTg->items),
(struct wormFeature**)(&tg->items) );
}
else
{
separateEmbryonicAli(cdnaTg->items, (struct cdaAli **)(&cdnaTg->items),
(struct cdaAli **)(&tg->items) );
}
}
struct trackGroup *getEmbryonicCdnaTg()
/* Get trackGroup for embryonic cDNA. For this to work, must be on after
* regular cDNA */
{
static struct trackGroup tg;
tgSetDefaults(&tg);
tg.checkBoxName = "embryoCdna";
tg.longLabel = "Embryonic cDNA Alignments";
tg.shortLabel = "Embryo cDNA";
tg.labelType = mtCdnaLabel;
tg.trackType = mtCdnaTrack;
tg.condensedType = mtCdnaCondensed;
tg.titleType = mtCdnaTitle;
tg.loadItems = embryonicCdnaLoadItems;
tg.itemName = tgCdnaName;
tg.drawItems = cdnaDrawItems;
tg.freeItems = cdnaFreeItems;
#ifdef COLOR_TRACKS
tg.color.r = 20;
tg.color.g = 110;
tg.color.b = 20;
tg.grayColor.r = 128;
tg.grayColor.g = 170;
tg.grayColor.b = 128;
#else
tg.grayColor.r = 140;
tg.grayColor.g = 140;
tg.grayColor.b = 140;
#endif /* COLOR_TRACKS */
return &tg;
}
void dynCdnaLoadItems(struct trackGroup *tg, boolean isCondensed, char *chrom, int start, int end)
/* Get all cdna item that was pasted in. */
{
struct cdaAli *dynList = NULL, *cda;
FILE *cdaFile = cdaOpenVerify(ti.dynCda);
/* Load alignments from pasted file. */
while ((cda = cdaLoadOne(cdaFile)) != NULL)
{
if (sameString(chromNames[cda->chromIx], ti.chrom) &&
!(ti.chromStart >= cda->chromEnd ||
ti.chromEnd <= cda->chromStart ))
{
slAddHead(&dynList, cda);
}
else
{
cdaFreeAli(cda);
}
}
slReverse(&dynList);
fclose(cdaFile);
if (isCondensed)
{
/* If condensed convert alignments to features. */
struct wormFeature *list = NULL, *el;
for (cda = dynList; cda != NULL; cda = cda->next)
{
el = newWormFeature("", chromNames[cda->chromIx], cda->chromStart, cda->chromEnd, 0);
slAddHead(&list, el);
}
tg->items = list;
cdaFreeAliList(&dynList);
}
else
{
/* Else just store alignment list. */
tg->items = dynList;
}
}
struct trackGroup *getDynCdnaTg()
/* Get trackGroup for WormAlign cDNA.*/
{
static struct trackGroup tg;
tgSetDefaults(&tg);
tg.checkBoxName = "dynCda";
tg.longLabel = "WormAlign";
tg.shortLabel = "WormAlign";
tg.labelType = mtDynLabel;
tg.trackType = mtDynTrack;
tg.condensedType = mtDynCondensed;
tg.titleType = mtDynTitle;
tg.loadItems = dynCdnaLoadItems;
tg.itemName = tgCdnaName;
tg.drawItems = cdnaDrawItems;
tg.freeItems = cdnaFreeItems;
#ifdef COLOR_TRACKS
tg.color.r = 128;
tg.color.g = 0;
tg.color.b = 128;
tg.grayColor.r = 160;
tg.grayColor.g = 128;
tg.grayColor.b = 160;
#else
tg.grayColor.r = 140;
tg.grayColor.g = 140;
tg.grayColor.b = 140;
#endif /* COLOR_TRACKS */
tg.subType = TRUE;
return &tg;
}
struct geneGroupCustom
/* This helps manage special needs of groups which display
* splicing diagrams. */
{
char *gdfDir;
struct wormGdfCache *gdfCache;
};
static int geneTotalHeight(struct trackGroup *tg, MgFont *font)
/* Most track groups will use this to figure out the height they use. */
{
tg->lineHeight = mgFontLineHeight(font)*2;
tg->heightPer = tg->lineHeight - 1;
tg->height = slCount(tg->items) * tg->lineHeight;
return tg->height;
}
struct gdfGene *loadGdfs(struct wormFeature *featList, struct geneGroupCustom *custom)
/* Make a gdfGene list corresponding to featList. */
{
struct gdfGene *list = NULL, *el;
struct wormFeature *name;
for (name = featList; name != NULL; name = name->next)
{
el = wormGetSomeGdfGene(name->name, custom->gdfCache);
slAddHead(&list, el);
}
slReverse(&list);
return list;
}
static void geneDrawItems(struct trackGroup *tg, FILE *mapFile, int chromStart, int chromEnd,
struct memGfx *mg, int xOff, int yOff, int width,
MgFont *font, Color color, Color grayColor)
/* Draw gene splicing diagram items. */
{
struct geneGroupCustom *custom = tg->customPt;
int baseWidth = chromEnd - chromStart;
struct wormFeature *feat;
struct wormFeature *featList = tg->items;
struct gdfGene *geneList = loadGdfs(featList, custom);
struct gdfGene *gene;
int y = yOff;
int heightPer = tg->heightPer;
int lineHeight = tg->lineHeight;
for (gene = geneList, feat = featList; gene != NULL; gene = gene->next, feat = feat->next)
{
boolean relRc = (ti.strand != gene->strand);
int exonInc = (relRc ? -1 : 1);
int exonIx = (relRc ? gene->dataCount/2 : 1);
struct gdfDataPoint *s;
int i;
int midY = y + heightPer/2;
int startX = 0x7fffffff;
int endX = -startX;
fixOffsets(gene, chromStart, chromEnd, ti.strand, feat->start, feat->end);
for (i=1; i<gene->dataCount-1; i += 2)
{
int x1, x2;
int midX;
s = &gene->dataPoints[i];
x1 = roundingScale(s[0].start, width, baseWidth) + xOff;
x2 = roundingScale(s[1].start, width, baseWidth) + xOff;
midX = (x1+x2)/2;
mgDrawLine(mg, x1, midY, midX, y, color);
mgDrawLine(mg, midX, y, x2, midY, color);
}
for (i=0; i<gene->dataCount; i+=2)
{
int x1, x2, w, h;
char tbuf[5];
int twid;
/* Draw exon box. */
s = &gene->dataPoints[i];
x1 = roundingScale(s[0].start, width, baseWidth) + xOff;
x2 = roundingScale(s[1].start, width, baseWidth) + xOff;
w = x2-x1;
if (w < 1) w = 1;
h = y+heightPer-midY;
mgDrawBox(mg, x1, midY, w, h, color);
/* Draw exon number in middle if exon is big enough. */
sprintf(tbuf, "%d", exonIx);
twid = mgFontStringWidth(font, tbuf);
if (w >= twid)
{
mgTextCentered(mg, x1, midY, w, h,
MG_WHITE, font, tbuf);
}
exonIx += exonInc;
/* Keep track of actual gene boundarys in pixel coordinates. */
if (startX > x1)
startX = x1;
if (endX < x2)
endX = x2;
}
mapWriteBox(mapFile, tg->subType, startX, y, endX-startX, lineHeight, gene->name, gene->strand);
mapWriteBox(mapFile, tg->trackType, xOff, y, width, lineHeight, gene->name, gene->strand);
y += lineHeight;
}
gdfFreeGeneList(&geneList);
}
void geneLoadItems(struct trackGroup *tg, boolean isCondensed, char *chrom, int start, int end)
/* Get gene names that intersect segment. */
{
struct geneGroupCustom *custom = tg->customPt;
tg->items = stripNamelessClusters(
wormSomeGenesInRange(ti.chrom, ti.chromStart, ti.chromEnd,custom->gdfDir));
}
struct trackGroup *getAceGenesTg()
/* Get track group for ACE genes. */
{
static struct trackGroup tg;
static struct geneGroupCustom custom;
static boolean initted = FALSE;
if (!initted)
{
initted = TRUE;
tgSetDefaults(&tg);
tg.checkBoxName = "spliDi";
tg.longLabel = "AceDB Gene Predictions";
tg.shortLabel = "AceDB Genes";
tg.labelType = mtGeneLabel;
tg.trackType = mtGeneTrack;
tg.subType = mtGeneOnBox;
tg.condensedType = mtGeneCondensed;
tg.titleType = mtGeneTitle;
tg.loadItems = geneLoadItems;
tg.itemName = tgFeatureName;
tg.totalHeight = geneTotalHeight;
tg.drawItems = geneDrawItems;
tg.drawCondensedItems = tgDrawCondensedFeatures;
tg.freeItems = tgFreeFeatures;
#ifdef COLOR_TRACKS
tg.color.r = 0;
tg.color.g = 0;
tg.color.b = 200;
#endif
custom.gdfDir = wormSangerDir();
custom.gdfCache = &wormSangerGdfCache;
tg.customPt = &custom;
}
return &tg;
}
boolean genieExists()
/* Return TRUE if genie predictions are in database. */
{
char testFile[512];
sprintf(testFile, "%sgenes.gdf", wormGenieDir());
return fileExists(testFile);
}
struct trackGroup *getGenieGenesTg()
/* Get track group for ACE genes. */
{
static struct trackGroup tg;
static struct geneGroupCustom custom;
static boolean initted = FALSE;
if (!initted)
{
initted = TRUE;
tgSetDefaults(&tg);
tg.checkBoxName = "genie";
tg.longLabel = "Genie Gene Predictions";
tg.shortLabel = "Genie Genes";
tg.labelType = mtGeneLabel;
tg.trackType = mtGeneTrack;
tg.subType = mtGeneOnBox;
tg.condensedType = mtGeneCondensed;
tg.titleType = mtGeneTitle;
tg.loadItems = geneLoadItems;
tg.itemName = tgFeatureName;
tg.totalHeight = geneTotalHeight;
tg.drawItems = geneDrawItems;
tg.drawCondensedItems = tgDrawCondensedFeatures;
tg.freeItems = tgFreeFeatures;
#ifdef COLOR_TRACKS
tg.color.r = 160;
tg.color.g = 110;
tg.color.b = 0;
#endif
custom.gdfDir = wormGenieDir();
custom.gdfCache = &wormGenieGdfCache;
tg.customPt = &custom;
}
return &tg;
}
static void getHidAlignedToTarget(char *hSym, char *tSym, int symCount,
char **retHid, int *retCount)
/* Make a copy of hidden symbols omitting those that correspond to
* inserts in target. */
{
char *hid = needMem(symCount);
int hidCount = 0;
int i;
for (i=0; i<symCount; ++i)
{
if (tSym[i] != '-')
hid[hidCount++] = hSym[i];
}
*retHid = hid;
*retCount = hidCount;
}
static void xenoDraw(struct memGfx *mg, int x, int w, int y, int h,
Color strongColor, Color weakColor, struct xaAli *xa)
/* Draw xeno-alignment in the indicated box */
{
int startIx = -1;
enum state {skip, weak, strong} state = skip, lastState = skip;
int i;
char *hSym = xa->hSym;
int symCount = xa->symCount;
int hidCount;
char *hid;
char c;
Color col;
int x1, x2;
getHidAlignedToTarget(hSym, xa->tSym, symCount, &hid, &hidCount);
if (ti.strand == '-')
reverseBytes(hid, hidCount);
for (i=0; i<=hidCount; ++i)
{
c = hid[i];
if (i == hidCount)
state = skip;
else
{
switch (c)
{
case 'Q':
case 'T':
state = skip;
break;
case '1':
case '2':
case '3':
case 'H':
state = strong;
break;
case 'L':
state = weak;
break;
}
}
if (state != lastState)
{
if (lastState != skip)
{
x1 = roundingScale(startIx, w, hidCount);
x2 = roundingScale(i, w, hidCount);
col = (lastState == strong ? strongColor : weakColor);
mgDrawBox(mg, x + x1, y, x2-x1, h, col);
}
lastState = state;
startIx = i;
}
}
freeMem(hid);
}
static void briggsaeDrawItems(struct trackGroup *tg, FILE *mapFile, int chromStart, int chromEnd,
struct memGfx *mg, int xOff, int yOff, int width,
MgFont *font, Color color, Color grayColor)
/* Draw a xeno alignment list. */
{
int x1,x2,w;
int baseWidth = chromEnd - chromStart;
double scaleFactor = (double)width/baseWidth;
struct xaAli *xa;
int y = yOff;
for (xa = tg->items; xa != NULL; xa = xa->next)
{
int start, end;
strandFixupPair(xa->tStart, xa->tEnd, &start, &end);
x1 = (int)(scaleFactor * (start-chromStart)) + xOff;
x2 = (int)(scaleFactor * (end-chromStart)) + xOff;
w = x2-x1;
xenoDraw(mg, x1, w, y, tg->heightPer, color, grayColor, xa);
mapWriteBox(mapFile, tg->subType, x1, y, w, tg->lineHeight, xa->name, ti.strand);
mapWriteBox(mapFile, tg->trackType, xOff, y, width, tg->lineHeight, xa->name, ti.strand);
y += tg->lineHeight;
}
}
static void briggsaeDrawCondensedItems(struct trackGroup *tg, FILE *mapFile, int chromStart, int chromEnd,
struct memGfx *mg, int xOff, int yOff, int width, int height,
MgFont *font, Color color, Color grayColor)
/* Draw a xeno alignment list in condensed format. */
{
int x1,x2,w;
struct xaAli *xa;
int baseWidth = chromEnd - chromStart;
double scaleFactor = (double)width/baseWidth;
Color c;
for (xa = tg->items; xa != NULL; xa = xa->next)
{
int start, end;
strandFixupPair(xa->tStart, xa->tEnd, &start, &end);
x1 = (int)(scaleFactor * (start-chromStart));
x2 = (int)(scaleFactor * (end-chromStart));
w = x2-x1;
if (w < 1) w = 1;
c = (xa->milliScore >= 500 ? color : grayColor);
mgDrawBox(mg, x1+xOff, yOff, w, height, c);
}
mapWriteBox(mapFile, tg->condensedType, xOff, yOff, width, height, "", '+');
}
void briggsaeLoadItems(struct trackGroup *tg, boolean isCondensed, char *chrom, int start, int end)
/* Load in C. briggsae alignment data that's in range. */
{
char indexFileName[512];
char dataFileName[512];
char *xDir;
char *cb = "cbriggsae";
struct xaAli *xaList;
xDir = wormXenoDir();
sprintf(indexFileName, "%s%s/%s%s", xDir, cb, chrom, xaChromIxSuffix());
sprintf(dataFileName, "%s%s/all%s", xDir, cb, xaAlignSuffix());
tg->items = xaList = xaReadRange(indexFileName, dataFileName, start, end, isCondensed);
}
static void briggsaeFreeItems(struct trackGroup *tg, boolean isCondensed)
/* Free simple fireatures list. */
{
xaAliFreeList((struct xaAli**)(&tg->items));
}
char *briggsaeItemName(struct trackGroup *tg, void *item)
/* Return name of a briggsae item. */
{
struct xaAli *xa = item;
return xa->name;
}
struct trackGroup *getBriggsaeTg()
/* Get track group for C. briggsae homologies. */
{
static struct trackGroup tg;
static boolean initted = FALSE;
if (!initted)
{
tg.checkBoxName = "briggsae";
tg.longLabel = "C. briggsae homologies";
tg.shortLabel = "C. briggsae";
tg.labelType = mtBriggsaeLabel;
tg.trackType = mtBriggsaeTrack;
tg.subType = mtBriggsaeOnBox;
tg.condensedType = mtBriggsaeCondensed;
tg.titleType = mtBriggsaeTitle;
#ifdef COLOR_TRACKS
tg.color.r = 140;
tg.color.g = 0;
tg.color.b = 200;
tg.grayColor.r = 210;
tg.grayColor.g = 140;
tg.grayColor.b = 250;
#else
tg.grayColor.r = 140;
tg.grayColor.g = 140;
tg.grayColor.b = 140;
#endif /* COLOR_TRACKS */
tg.loadItems = briggsaeLoadItems;
tg.itemName = briggsaeItemName;
tg.totalHeight = tgUsualHeight;
tg.drawItems = briggsaeDrawItems;
tg.drawCondensedItems = briggsaeDrawCondensedItems;
tg.freeItems = briggsaeFreeItems;
}
return &tg;
}
static long figureTickSpan(long totalLength, int maxNumTicks)
/* Figure out whether ticks on ruler should be 1, 5, 10, 50, 100, 500,
* 1000, etc. units apart. */
{
int roughTickLen = totalLength/maxNumTicks;
int i;
int tickLen = 1;
for (i=0; i<9; ++i)
{
if (roughTickLen < tickLen)
return tickLen;
tickLen *= 5;
if (roughTickLen < tickLen)
return tickLen;
tickLen *= 2;
}
return 1000000000;
}
static void numLabelString(int num, char *label)
/* Returns a numerical labeling string. */
{
char *sign = "";
if (num < 0)
{
num = -num;
sign = "-";
}
sprintf(label, "%s%d", sign, num);
}
void mgDrawRulerBumpText(struct memGfx *mg, int xOff, int yOff,
int height, int width,
Color color, MgFont *font,
int startNum, int range, int bumpX, int bumpY)
/* Draw a ruler inside the indicated part of mg with numbers that start at
* startNum and span range. Bump text positions slightly. */
{
int tickSpan;
int tickPos;
double scale;
int firstTick;
int remainder;
int end = startNum + range;
int x;
char tbuf[14];
int numWid;
int goodNumTicks;
int niceNumTicks = width/35;
numLabelString(startNum+range, tbuf);
numWid = mgFontStringWidth(font, tbuf)+4+bumpX;
goodNumTicks = width/numWid;
if (goodNumTicks < 1) goodNumTicks = 1;
if (goodNumTicks > niceNumTicks) goodNumTicks = niceNumTicks;
tickSpan = figureTickSpan(range, goodNumTicks);
scale = (double)width / range;
firstTick = startNum + tickSpan;
remainder = firstTick % tickSpan;
firstTick -= remainder;
for (tickPos=firstTick; tickPos<end; tickPos += tickSpan)
{
numLabelString(tickPos, tbuf);
numWid = mgFontStringWidth(font, tbuf)+4;
x = (int)((tickPos-startNum) * scale) + xOff;
mgDrawBox(mg, x, yOff, 1, height, color);
if (x - numWid >= xOff)
{
mgTextCentered(mg, x-numWid + bumpX, yOff + bumpY, numWid,
height, color, font, tbuf);
}
}
}
void mgDrawRuler(struct memGfx *mg, int xOff, int yOff, int height, int width,
Color color, MgFont *font,
int startNum, int range)
/* Draw a ruler inside the indicated part of mg with numbers that start at
* startNum and span range. */
{
mgDrawRulerBumpText(mg, xOff, yOff, height, width, color, font,
startNum, range, 0, 0);
}
void drawTracks(char *where, struct trackGroup *groupList, boolean compactDisplay)
/* Draw tracks onto a gif in memory and save it. */
{
struct trackGroup *group;
int baseWidth = ti.chromEnd - ti.chromStart;
MgFont *font = mgSmallFont();
struct memGfx *mg;
struct tempName gifTn, mapTn;
FILE *mapFile;
int fontHeight = mgFontLineHeight(font);
int insideHeight = fontHeight-1;
int border = 1;
int xOff = border;
int pixWidth, pixHeight;
int insideWidth;
int y;
int typeCount = slCount(groupList);
int labelCount = typeCount;
int leftLabelWidth = 0;
int rulerHeight = fontHeight;
int relNumOff;
/* Figure out dimensions and allocate drawing space. */
if (!ti.withCenterLabels)
labelCount = 0;
if (ti.withLeftLabels)
{
leftLabelWidth = 98;
xOff += leftLabelWidth + border;
}
pixWidth = 616;
insideWidth = pixWidth-border-xOff;
if (compactDisplay)
{
pixHeight = rulerHeight + (typeCount+labelCount)*fontHeight + border;
for (group = groupList; group != NULL; group = group->next)
group->height = fontHeight;
}
else
{
pixHeight = border + rulerHeight + fontHeight*labelCount;
for (group = groupList; group != NULL; group = group->next)
{
pixHeight += group->totalHeight(group, font);
}
}
mg = mgNew(pixWidth, pixHeight);
mgClearPixels(mg);
/* Start writing an image map */
makeTempName(&mapTn, "trk", ".map");
mapFile = mustOpen(mapTn.forCgi, "wb");
mapWriteHead(mapFile, ti.title, pixWidth, pixHeight,
where, ti.chrom, ti.chromStart, ti.chromEnd, ti.strand,
ti.hilite, ti.relStart);
/* Find colors to draw in. */
for (group = groupList; group != NULL; group = group->next)
{
group->ixColor = mgFindColor(mg, group->color.r, group->color.g, group->color.b);
group->ixGrayColor = mgFindColor(mg, group->grayColor.r, group->grayColor.g, group->grayColor.b);
}
/* Draw cyan hilite background. */
if (ti.hilite)
{
drawHilite(mg, xOff, border, insideWidth, pixHeight-2*border, ti.hilite);
}
/* Draw left labels. */
if (ti.withLeftLabels)
{
int inWid = xOff-border*3;
mgDrawBox(mg, xOff-border*2, 0, border, pixHeight, mgFindColor(mg, 0, 0, 200));
mgSetClip(mg, border, border, inWid, pixHeight-2*border);
y = border;
for (group = groupList; group != NULL; group = group->next)
{
struct slList *item;
if (ti.withCenterLabels)
y += fontHeight;
if (compactDisplay)
{
mgTextCentered(mg, border, y, inWid, fontHeight, group->ixColor, font, group->shortLabel);
y += fontHeight;
}
else
{
for (item = group->items; item != NULL; item = item->next)
{
char *name = group->itemName(group, item);
mgTextCentered(mg, border, y, inWid, group->lineHeight, group->ixColor, font, name);
mapWriteBox(mapFile, group->labelType, border, y, inWid, group->lineHeight, name, '.');
y += group->lineHeight;
}
}
}
}
/* Draw center labels. */
if (ti.withCenterLabels)
{
mgSetClip(mg, xOff, border, insideWidth, pixHeight - 2*border);
y = border;
for (group = groupList; group != NULL; group = group->next)
{
mgTextCentered(mg, xOff, y+1, insideWidth, insideHeight, group->ixColor, font, group->longLabel);
mapWriteBox(mapFile, group->titleType, xOff, y, insideWidth, fontHeight, "", '.');
y += fontHeight;
y += group->height;
}
}
/* Draw tracks. */
y = 1;
for (group = groupList; group != NULL; group = group->next)
{
if (ti.withCenterLabels)
y += fontHeight;
mgSetClip(mg, xOff, y, insideWidth, group->height);
if (compactDisplay)
{
mgSetClip(mg, xOff, y, insideWidth, fontHeight);
group->drawCondensedItems(group, mapFile, ti.chromStart, ti.chromEnd,
mg, xOff, y, insideWidth, insideHeight,
font, group->ixColor, group->ixGrayColor);
}
else
{
group->drawItems(group, mapFile, ti.chromStart, ti.chromEnd,
mg, xOff, y, insideWidth,
font, group->ixColor, group->ixGrayColor);
}
y += group->height;
}
/* Show ruler at bottom. */
y = pixHeight - rulerHeight;
mgSetClip(mg, xOff, y, insideWidth, rulerHeight);
relNumOff = ti.chromStart - ti.relStart;
if (ti.strand == '-')
relNumOff = -relNumOff;
mgDrawRuler(mg, xOff, y, rulerHeight, insideWidth, MG_BLACK, font,
relNumOff, baseWidth);
mapWriteBox(mapFile, mtRuler, xOff, y, insideWidth, rulerHeight, "", '.');
/* Finish up image map. */
mapWriteBox(mapFile, mtEnd, 0,0,0,0,"",'.');
fclose(mapFile);
chmod(mapTn.forCgi, 0666);
/* Save out picture and tell html file about it. */
makeTempName(&gifTn, "trk", ".gif");
-mgSaveGif(mg, gifTn.forCgi);
+mgSaveGif(mg, gifTn.forCgi, FALSE);
printf("<INPUT TYPE=HIDDEN NAME=map VALUE=\"%s\">\n", mapTn.forCgi);
printf(
"<P><INPUT TYPE=IMAGE SRC = \"%s\" BORDER=1 WIDTH=%d HEIGHT=%d NAME = \"mouse\" ALIGN=BOTTOM><BR>\n",
gifTn.forHtml, pixWidth, pixHeight);
mgFree(&mg);
chmod(gifTn.forCgi, 0666);
}
void makeNumText(char *name, int num, int digits)
/* Make a text control filled with a number. */
{
printf("<INPUT TYPE=TEXT NAME=\"%s\" SIZE=%d VALUE=%d>\n", name, digits, num);
}
void makeText(char *name, char *initialVal, int chars)
/* Make a text control filled with initial value. */
{
printf("<INPUT TYPE=TEXT NAME=\"%s\" SIZE=%d VALUE=%s>\n", name, chars, initialVal);
}
void makeSubmitButton(char *name, char *value)
/* Make a submit button. */
{
printf("<INPUT TYPE=SUBMIT NAME=\"%s\" VALUE=\"%s\">\n", name, value);
}
void makeDropList(char *name, char *menu[], int menuSize, char *checked)
/* Make a drop-down list. */
{
int i;
char *selString;
if (checked == NULL) checked = menu[0];
printf("<SELECT ALIGN=CENTER NAME=\"%s\">\n", name);
for (i=0; i<menuSize; ++i)
{
if (!differentWord(menu[i], checked))
selString = " SELECTED";
else
selString = "";
printf("<OPTION%s>%s</OPTION>\n", selString, menu[i]);
}
printf("</SELECT>\n");
}
void makeCheckBox(char *name, boolean isChecked)
/* Create a checkbox with the given name in the given state. */
{
printf("<INPUT TYPE=CHECKBOX NAME=\"%s\" VALUE=on%s>\n", name,
(isChecked ? " CHECKED" : "") );
}
void makeHiddenString(char *varName, char *string)
/* Store string in hidden input for next time around. */
{
printf("<INPUT TYPE=HIDDEN NAME=\"%s\" VALUE=\"%s\">\n", varName, string);
}
void continueHiddenVar(char *varName)
/* Write CGI var back to hidden input for next time around. */
{
if (cgiVarExists(varName))
makeHiddenString(varName, cgiString(varName));
}
char *strandMenu[] = {"+", "-"};
void checkTitle()
/* If title doesn't exist, make it up from locus. */
{
if (ti.title == NULL)
{
if( cgiVarExists("jump_to"))
snprintf(titleBuf, sizeof(titleBuf), "WS120 position: %s:%d-%d size %d strand %c", ti.chrom, ti.chromStart, ti.chromEnd, ti.chromEnd - ti.chromStart, ti.strand );
else if (ti.locus && 0 < strlen(ti.locus))
snprintf(titleBuf, sizeof(titleBuf), "WS120 Region Near: %s", ti.locus );
else
snprintf(titleBuf, sizeof(titleBuf), "WS120 Tracks Display");
ti.title = titleBuf;
}
}
void doTracks(boolean gotMap)
/* Lookup chromosome position of ti.locus and do tracks around it. */
{
struct trackGroup *groupList = NULL, *group;
int baseWidth;
boolean doCompact;
char strandString[2];
boolean gotGenie = genieExists();
char * browserChr;
wormChromNames(&chromNames, &chromCount);
/* Get input vars from menu items. */
ti.withCosmids = cgiBoolean("cosmids");
ti.withCdna = cgiBoolean("cDNA");
ti.withEmbryoCdna = cgiBoolean("embryoCdna");
ti.withGenes = cgiBoolean("spliDi");
ti.withGenie = cgiBoolean("genie");
ti.withBriggsae = cgiBoolean("briggsae");
ti.withLeftLabels = cgiBoolean("leftLabels");
ti.withCenterLabels = cgiBoolean("centerLabels");
ti.whereHid = cgiOptionalString("whereHid");
ti.dynFa = cgiOptionalString("dynFa");
ti.dynCda = cgiOptionalString("dynCda");
/* Other input vars may be set from map file. Otherwise get it from cgi. */
if (!gotMap)
{
if (ti.locus == NULL)
ti.locus = trimSpaces(cgiString("where"));
ti.title = cgiOptionalString("title");
checkTitle();
/* If they didn't ask for either cDNA or splicing diagram - hah!
* give them both the first time through. Why? Saves space in
* computer generated files calling tracker mostly. */
if (!ti.withCdna && !ti.withEmbryoCdna && !ti.withGenes && !ti.withCosmids)
{
ti.withCdna = TRUE;
ti.withEmbryoCdna = TRUE;
ti.withGenes = TRUE;
ti.withGenie = TRUE;
ti.withBriggsae = TRUE;
ti.withCenterLabels = TRUE;
ti.withLeftLabels = TRUE;
}
/* Figure out bases covered by track. */
if (!wormGeneRange(ti.locus, &ti.chrom, &ti.strand, &ti.chromStart, &ti.chromEnd)) {
char * wh;
wh = trimSpaces(cgiString("where"));
errAbort("Couldn't find locus '%s' ('%s', '%s')", ti.locus, ti.whereHid, wh);
}
if (!wormIsChromRange(ti.locus))
{
ti.chromStart -= 500;
ti.chromEnd += 500;
}
if (ti.chromStart == ti.chromEnd)
{
ti.chromStart -= 10;
ti.chromEnd += 10;
}
/* Set up hiliting range. */
if (cgiVarExists("hilite"))
{
ti.hilite = cgiString("hilite");
}
/* Set up numbering to initially start at zero. */
ti.relStart = ti.chromStart;
}
wormClipRangeToChrom(ti.chrom, &ti.chromStart, &ti.chromEnd);
baseWidth = ti.chromEnd-ti.chromStart;
doCompact = (baseWidth >= 50000);
/* Figure out which track groups to use. */
if (ti.dynCda)
slAddTail(&groupList, getDynCdnaTg());
if (ti.withCosmids)
slAddTail(&groupList, getCosmidTg());
if (ti.withGenes)
slAddTail(&groupList, getAceGenesTg());
if (gotGenie && ti.withGenie)
slAddTail(&groupList, getGenieGenesTg());
if (ti.withBriggsae)
slAddTail(&groupList, getBriggsaeTg());
if (ti.withCdna)
slAddTail(&groupList, getCdnaTg());
if (ti.withEmbryoCdna)
slAddTail(&groupList, getEmbryonicCdnaTg());
/* Tell groups to load their items. */
for (group = groupList; group != NULL; group = group->next)
group->loadItems(group, doCompact, ti.chrom, ti.chromStart, ti.chromEnd);
/* Deal with ti.strand issues. Explicit parameter will over-ride
* what we got with the DNA. But if the DNA is ambiguous try
* and find a dominant ti.strand in the gene list. If all else
* fails set it to '+' */
if (!gotMap)
{
ti.strand = dominantStrand(getAceGenesTg()->items);
if (ti.strand == '.' && cgiVarExists("strand"))
{
ti.strand = cgiString("strand")[0];
}
if (ti.strand == '.')
{
ti.strand = '+';
}
}
/* Tell browser where to go when they click on image. */
printf("<FORM ACTION=\"%stracksWS120.exe\">\n\n", cgiDir());
/* Perpetuate some hidden variables. */
ti.whereHid = trimSpaces(cgiString("where"));
if( (char *) NULL != ti.whereHid && 0 < strlen(ti.whereHid)) {
printf("<INPUT TYPE=HIDDEN NAME=\"whereHid\" VALUE=\"%s\">\n", ti.whereHid);
}
continueHiddenVar("dynFa");
continueHiddenVar("dynCda");
/* Center everything from now on. */
printf("<CENTER>\n");
/* Show title . */
printf("<H2>%s</H2>\n", ti.title);
/* Put up scroll and zoom controls. */
fputs("move ", stdout);
makeSubmitButton("left3", "<<<");
makeSubmitButton("left2", " <<");
makeSubmitButton("left1", " < ");
makeSubmitButton("right1", " > ");
makeSubmitButton("right2", ">> ");
makeSubmitButton("right3", ">>>");
fputs(" zoom in ", stdout);
makeSubmitButton("in1", "1.5x");
makeSubmitButton("in2", " 3x ");
makeSubmitButton("in3", "10x");
fputs(" zoom out ", stdout);
makeSubmitButton("out1", "1.5x");
makeSubmitButton("out2", " 3x ");
makeSubmitButton("out3", "10x");
fputs("<BR>\n", stdout);
drawTracks(ti.locus, groupList, doCompact);
fputs("Click on image to view more information on a gene or alignment. Click on ruler for DNA<BR>", stdout);
if (ti.hilite)
fputs("Unusual region is hilighted light blue.<BR>\n", stdout);
/* Put up control panel */
fputs("<HR ALIGN=CENTER>\n",stdout);
fputs("Chromosome ", stdout);
makeDropList("chrom", chromNames, chromCount, ti.chrom);
fputs(" bases ",stdout);
makeNumText("start", ti.chromStart, 9);
fputs(" - ", stdout);
makeNumText("end", ti.chromEnd, 9);
fputs(" strand ", stdout);
strandString[0] = ti.strand;
strandString[1] = 0;
makeDropList("strand", strandMenu, 2, strandString);
fputs(" ", stdout);
makeSubmitButton("jump_to", "jump");
fputs("<BR>\n", stdout);
fputs("<B>View:</B>", stdout);
fputs(" Cosmids ", stdout);
makeCheckBox("cosmids", ti.withCosmids);
fputs(" AceDb Gene Predictions ", stdout);
makeCheckBox("spliDi", ti.withGenes);
if (gotGenie)
{
fputs("Genie Gene Predictons ", stdout);
makeCheckBox("genie", ti.withGenie);
}
fputs(" cDNA ", stdout);
makeCheckBox("cDNA", ti.withCdna);
fputs("<BR>\nseparate embryonic cDNA ", stdout);
makeCheckBox("embryoCdna", ti.withEmbryoCdna);
fputs(" <I>C. briggsae</I> homologies ", stdout);
makeCheckBox("briggsae", ti.withBriggsae);
fputs("<BR>\nLeft Labels ", stdout);
makeCheckBox("leftLabels", ti.withLeftLabels);
fputs(" Center Labels", stdout);
makeCheckBox("centerLabels", ti.withCenterLabels);
makeSubmitButton("refresh", "refresh");
fputs("<BR>\n", stdout);
fputs("Jump to named ORF or gene of known sequence: ",stdout);
makeText("where", "", 16);
fputs(" ", stdout);
makeSubmitButton("go_to_where", "jump");
fputs("</P>\n", stdout);
fputs("<P>Return to <A HREF=\"../IntronWS120/index.html\">Intronerator Gateway WS120</A></P>\n", stdout);
browserChr = cloneStringZ(ti.chrom,5);
touppers(browserChr);
printf("<P>Link to Genome Browser <A HREF=\"http://genome-test.cse.ucsc.edu/cgi-bin/hgTracks?db=ce2&org=C.+elegans&position=chr%s:%d-%d\" target=_blank> chr%s:%d-%d </A><BR>\n", browserChr, ti.chromStart, ti.chromEnd, browserChr, ti.chromStart, ti.chromEnd);
free(browserChr);
for (group = groupList; group != NULL; group = group->next)
group->freeItems(group, doCompact);
}
void zoomAroundCenter(double amount)
/* Set ends so as to zoom around center by scaling amount. */
{
int baseCount = ti.chromEnd - ti.chromStart;
int center = (ti.chromStart + ti.chromEnd)/2;
int newCount = (int)(baseCount*amount + 0.5);
if (newCount < 30) newCount = 30;
ti.chromStart = center - newCount/2;
ti.chromEnd = ti.chromStart + newCount;
}
void zoomInLots(double relCenter, int zoomFactor)
/* Zoom in around relative center position by zoomFactor. */
{
int oldWinWidth = ti.chromEnd - ti.chromStart;
int center = ti.chromStart + round(relCenter*oldWinWidth);
int newWinWidth = oldWinWidth/zoomFactor;
ti.chromStart = center - newWinWidth/2;
ti.chromEnd = ti.chromStart + newWinWidth;
}
void relativeScroll(double amount)
/* Scroll percentage of visible window. */
{
int baseCount = ti.chromEnd - ti.chromStart;
int offset;
if (ti.strand == '-')
amount = -amount;
offset = (int)(amount * baseCount + 0.5);
ti.chromEnd += offset;
ti.chromStart += offset;
}
void dispatcher()
/* Peep at cgi string to see if we should just quickly pass the buck to
* fuzzyFind.exe or getGene.exe, or handle it ourselves. */
{
enum mapType mt;
int boxX, boxY, boxW, boxH;
char *boxName;
char boxStrand;
bits32 pixWidth, pixHeight;
boolean gotMap = FALSE;
boolean resetTitle = FALSE;
if (cgiVarExists("map"))
{
char *mapName = cgiString("map");
FILE *f = mustOpen(mapName, "rb");
gotMap = TRUE;
mapReadHead(f, &ti.title, &pixWidth, &pixHeight,
&ti.locus, &ti.chrom, &ti.chromStart, &ti.chromEnd, &ti.strand,
&ti.hilite, &ti.relStart);
if (cgiVarExists("refresh"))
{
char * prevLocus = (char *) NULL;
if (cgiVarExists("whereHid"))
{
prevLocus = trimSpaces(cgiString("whereHid"));
}
else if (cgiVarExists("where"))
{
prevLocus = trimSpaces(cgiString("where"));
}
if( prevLocus == NULL || 0 == strlen(prevLocus) )
{
ti.chrom = cgiString("chrom");
ti.chromStart = atoi(cgiString("start"));
ti.chromEnd = atoi(cgiString("end"));
ti.strand = cgiString("strand")[0];
ti.relStart = ti.chromStart;
resetTitle = TRUE;
}
else
{
gotMap = FALSE; /* Disregard map, go for gene. */
ti.locus = prevLocus;
ti.title = NULL;
}
}
else if (cgiVarExists("go_to_where"))
{
char *newLocus = trimSpaces(cgiString("where"));
if( newLocus == NULL || 0 == strlen(newLocus) )
{
if (cgiVarExists("whereHid"))
{
newLocus = trimSpaces(cgiString("whereHid"));
}
}
if( newLocus == NULL || 0 == strlen(newLocus) )
{
ti.chrom = cgiString("chrom");
ti.chromStart = atoi(cgiString("start"));
ti.chromEnd = atoi(cgiString("end"));
ti.strand = cgiString("strand")[0];
ti.relStart = ti.chromStart;
resetTitle = TRUE;
}
else
{
gotMap = FALSE; /* Disregard map, go for gene. */
ti.locus = newLocus;
ti.title = NULL;
}
}
else if( cgiVarExists("jump_to"))
{
ti.chrom = cgiString("chrom");
ti.chromStart = atoi(cgiString("start"));
ti.chromEnd = atoi(cgiString("end"));
ti.strand = cgiString("strand")[0];
ti.relStart = ti.chromStart;
resetTitle = TRUE;
}
else if (cgiVarExists("left3"))
{ relativeScroll(-0.95); resetTitle = TRUE; }
else if (cgiVarExists("left2"))
{ relativeScroll(-0.475); resetTitle = TRUE; }
else if (cgiVarExists("left1"))
{ relativeScroll(-0.1); resetTitle = TRUE; }
else if (cgiVarExists("right1"))
{ relativeScroll(0.1); resetTitle = TRUE; }
else if (cgiVarExists("right2"))
{ relativeScroll(0.475); resetTitle = TRUE; }
else if (cgiVarExists("right3"))
{ relativeScroll(0.95); resetTitle = TRUE; }
else if (cgiVarExists("in3"))
{ zoomAroundCenter(1.0/10.0); resetTitle = TRUE; }
else if (cgiVarExists("in2"))
{ zoomAroundCenter(1.0/3.0); resetTitle = TRUE; }
else if (cgiVarExists("in1"))
{ zoomAroundCenter(1.0/1.5); resetTitle = TRUE; }
else if (cgiVarExists("out1"))
{ zoomAroundCenter(1.5); resetTitle = TRUE; }
else if (cgiVarExists("out2"))
{ zoomAroundCenter(3.0); resetTitle = TRUE; }
else if (cgiVarExists("out3"))
{ zoomAroundCenter(10.0); resetTitle = TRUE; }
else
{
ti.mouseX = cgiInt("mouse.x");
ti.mouseY = cgiInt("mouse.y");
if (!mapScanForHit(f, ti.mouseX, ti.mouseY,
&mt, &boxX, &boxY, &boxW, &boxH, &boxName, &boxStrand))
{
mt = mtOutside;
}
switch (mt)
{
case mtCdnaTrack:
case mtCdnaLabel:
{
printf("Location: %sfuzzyFind.exe?cDNA=%s&gene=%s:%d-%d&hayStrand=%c\n\n",
cgiDir(), boxName, ti.chrom, ti.chromStart, ti.chromEnd, ti.strand);
return;
}
case mtDynTrack:
case mtDynLabel:
{
printf("Location: %sfuzzyFind.exe?needleFile=%s&stringency=cDNA&gene=%s:%d-%d&hayStrand=%c\n\n",
cgiDir(), cgiString("dynFa"), ti.chrom, ti.chromStart, ti.chromEnd, ti.strand);
return;
}
case mtGeneTrack:
case mtGeneLabel:
case mtCosmidTrack:
case mtCosmidLabel:
{
printf("Location: %sgetgene.exe?geneName=%s&litLink=on&intronsLowerCase=On\n\n",
cgiDir(), boxName);
return;
}
case mtGeneOnBox:
{
double relPos = (double)(ti.mouseX-boxX)/(double)(boxW);
if (ti.strand != boxStrand) relPos = 1.0 - relPos;
printf("Location: %sgetgene.exe?geneName=%s&hiliteNear=%f&litLink=on&intronsLowerCase=On\n\n",
cgiDir(), boxName, relPos);
return;
}
case mtBriggsaeTrack:
case mtBriggsaeLabel:
{
printf("Location: %sxaShow.exe?qOrganism=%s&tOrganism=%s&query=%s&target=%s:%d-%d&strand=%c\n\n",
cgiDir(), "cbriggsae", organism, boxName,
ti.chrom, ti.chromStart, ti.chromEnd, ti.strand);
}
return;
case mtBriggsaeOnBox:
{
double relPos = (double)(ti.mouseX-boxX)/(double)(boxW);
printf("Location: %sxaShow.exe?clickPos=%f&qOrganism=%s&tOrganism=%s&query=%s&target=%s:%d-%d&strand=%c\n\n",
cgiDir(), relPos, "cbriggsae", organism, boxName,
ti.chrom, ti.chromStart, ti.chromEnd, ti.strand);
}
return;
case mtRuler:
{
printf("Location: %sgetgene.exe?geneName=%s:%d-%d&litLink=on&intronsLowerCase=On&strand=%c\n\n",
cgiDir(), ti.chrom, ti.chromStart, ti.chromEnd, ti.strand);
return;
}
case mtDynCondensed:
case mtCosmidCondensed:
case mtGeneCondensed:
case mtCdnaCondensed:
case mtBriggsaeCondensed:
{
double relPos = (double)(ti.mouseX-boxX)/(double)(boxW);
if (ti.strand == '-')
relPos = 1.0-relPos;
zoomInLots(relPos, 10);
break;
}
default:
break;
}
}
fclose(f);
}
if( resetTitle )
{
snprintf(titleBuf, sizeof(titleBuf), "WS120 position: %s:%d-%d size %d strand %c", ti.chrom, ti.chromStart, ti.chromEnd, ti.chromEnd - ti.chromStart, ti.strand );
ti.title = titleBuf;
}
else
{
ti.title = (char *) NULL;
}
checkTitle();
htmlStart(ti.title);
doTracks(gotMap);
htmlEnd();
}
int main(int argc, char *argv[])
{
if (argc == 2 && strcmp(argv[1], "test") == 0)
{
putenv("QUERY_STRING=where=unc-52");
}
putenv("JKWEB=../htdocs/IntronWS120/");
dnaUtilOpen();
htmEmptyShell(dispatcher, "QUERY");
return 0;
}