src/hg/instinct/hgGeneset/drawingCode.c 1.5
1.5 2010/02/08 20:41:56 jsanborn
fixed silly bug
Index: src/hg/instinct/hgGeneset/drawingCode.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/instinct/hgGeneset/drawingCode.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -b -B -U 1000000 -r1.4 -r1.5
--- src/hg/instinct/hgGeneset/drawingCode.c 29 Jan 2010 22:27:35 -0000 1.4
+++ src/hg/instinct/hgGeneset/drawingCode.c 8 Feb 2010 20:41:56 -0000 1.5
@@ -1,522 +1,522 @@
/********************************************************************************/
/* Copyright 2007-2009 -- The Regents of the University of California */
/********************************************************************************/
#include <limits.h>
#include "common.h"
#include "bed.h"
#include "cart.h"
#include "customTrack.h"
#include "errCatch.h"
#include "genoLay.h"
#include "trackLayout.h"
#include "hash.h"
#include "hdb.h"
#include "hgColors.h"
#include "hPrint.h"
#include "htmshell.h"
#include "jsHelper.h"
#include "psGfx.h"
#include "trashDir.h"
#include "vGfx.h"
#include "hvGfx.h"
#include "web.h"
#include "hgHeatmapLib.h"
#include "heatmapUtility.h"
#include "featuresLib.h"
#include "hgStatsLib.h"
#include "filterFeatures.h"
#include "hgGenesets.h"
#define EXPR_DATA_SHADES 16
#define DEFAULT_MAX_DEVIATION 0.7
#define COLOR_SCALE 1.0
#define MIN_LABEL_SIZE 100 // minimum size of labels
#define TEXT_BUFFER 5 // pixels to put between text and heatmap
struct trackLayout tl; /* Dimensions of things, fonts, etc. */
/***** BEGIN HELPER *****/
#define CLIP(p,limit) if (p < 0) p = 0; if (p >= (limit)) p = (limit)-1;
void verticalTextLeft(struct vGfx *vg, int x, int y,
int width, int height, int colorIx, int fontHeight,
MgFont *font, char *string)
{
/* don't let this run wild */
//CLIP(width, vg->width);
//CLIP(height, vg->height);
if ((width <= 0) || (height <= 0))
return;
//y -= 5;
struct vGfx *vgHoriz;
int i, j;
/* reversed meanings of width and height here since this is going
* to rotate 90 degrees
*/
int offset = round((double) (width - fontHeight) / 2.0);
if (offset < 0)
offset = 0;
vgHoriz = vgOpenGif(height, width, "/dev/null", FALSE);
vgText(vgHoriz, 0, offset, colorIx, font, string);
/* now, blit from the horizontal to the vertical, rotate -90 (CCW) */
for (i = 0; i < height; i++) /* xSrc -> yDest */
{
int yDest = height - (i + 1);
for (j = 0; j < width; j++) /* ySrc -> xDest */
vgDot(vg, x + (j+1), y + yDest, vgGetDot(vgHoriz, i, j));
}
vgClose(&vgHoriz);
}
void vgMakeColorGradient(struct vGfx *vg,
struct rgbColor *start, struct rgbColor *end,
int steps, Color *colorIxs)
/* Make a color gradient that goes smoothly from start
* to end colors in given number of steps. Put indicesgl->chromLis
* in color table in colorIxs */
{
double scale = 0, invScale;
double invStep;
int i; int r,g,b;
steps -= 1; /* Easier to do the calculation in an inclusive way. */
invStep = 1.0/steps;
for (i=0; i<=steps; ++i)
{
invScale = 1.0 - scale;
r = invScale * start->r + scale * end->r;
g = invScale * start->g + scale * end->g;
b = invScale * start->b + scale * end->b;
colorIxs[i] = vgFindColorIx(vg, r, g, b);
scale += invStep;
}
}
char *cartSettingsString(char *prefix, char *functionName)
{
if (!prefix || !functionName)
return NULL;
struct hashEl *hEl = cartFindPrefix(cart, prefix);
if (!hEl)
return NULL;
struct dyString *dy = newDyString(1000);
while (hEl)
{
char *name = hEl->name;
char *val = hEl->val;
dyStringPrintf(dy, "%s=%s,", name, val);
hEl = hEl->next;
}
dyStringPrintf(dy, "%s,%s", functionName, VERSION);
char *str = dyStringCannibalize(&dy);
return str;
}
/***** END HELPER *****/
void drawRawData(struct vGfx *vg, struct rawData *rdList,
struct mapSettings *settings)
{
double gain = settings->gain;
double colorScale = COLOR_SCALE / settings->max_deviation;
double val;
double absVal;
Color valCol;
Color upShades[EXPR_DATA_SHADES];
Color downShades[EXPR_DATA_SHADES];
vgMakeColorGradient(vg, settings->zero, settings->high, EXPR_DATA_SHADES, upShades);
vgMakeColorGradient(vg, settings->zero, settings->low, EXPR_DATA_SHADES, downShades);
Color missingColor = vgFindColorIx(vg, settings->missing->r,
settings->missing->g, settings->missing->b);
vgSetClip(vg, 0, 0, settings->width, settings->height);
//vgBox(vg, 0, 0, settings->width, settings->height, zeroColor);
vgBox(vg, settings->hm_x, settings->hm_y,
settings->hm_width, settings->hm_height, missingColor);
vgBox(vg, settings->thumb_x, settings->thumb_y,
settings->thumb_width, settings->thumb_height, missingColor);
int i = -1, j = -1;
int prevSampleId = -1;
int prevFeatureId = -1;
char id[128];
char pixelStr[128];
struct hmPixel *hm, *hmList = NULL;
struct hashEl *el;
struct hash *pixelHash = hashNew(0);
struct rawData *rd;
for (rd = rdList; rd; rd = rd->next)
{
val = rd->val;
// Attempt to reduce number of hash calls by checking if we *just* searched
// for sample or feature id.
if (rd->sample_id != prevSampleId)
{
safef(id, sizeof (id), "%d", rd->sample_id);
i = hashIntValDefault(settings->x_index, id, -1);
prevSampleId = rd->sample_id;
}
if (rd->feature_id != prevFeatureId)
{
safef(id, sizeof (id), "%d", rd->feature_id);
j = hashIntValDefault(settings->y_index, id, -1);
prevFeatureId = rd->feature_id;
}
if (i < 0 || j < 0)
continue;
/** Draw big heatmap **/
int x1 = round((double) i * settings->hm_x_scale) + settings->hm_x;
int y1 = round((double) j * settings->hm_y_scale) + settings->hm_y;
int x2 = round((double) (i + 1) * settings->hm_x_scale) + settings->hm_x;
int y2 = round((double) (j + 1) * settings->hm_y_scale) + settings->hm_y;
safef(pixelStr, sizeof(pixelStr), "%d,%d", x1, y1);
if ((el = hashLookup(pixelHash, pixelStr)) == NULL)
{
hm = AllocA(struct hmPixel);
hm->x = x1;
hm->y = y1;
hm->w = ((x2 - x1) > 0) ? (x2 - x1) : 1;
hm->h = ((y2 - y1) > 0) ? (y2 - y1) : 1;
hm->val = 0.0;
hm->count = 0;
slAddHead(&hmList, hm);
hashAdd(pixelHash, pixelStr, hm);
}
else
hm = el->val;
hm->val += val;
hm->count += 1.0;
/** Draw Thumbnail in upper-left corner **/
x1 = round((double) i * settings->thumb_x_scale) + settings->thumb_x;
y1 = round((double) j * settings->thumb_y_scale) + settings->thumb_y;
x2 = round((double) (i + 1) * settings->thumb_x_scale) + settings->thumb_x;
y2 = round((double) (j + 1) * settings->thumb_y_scale) + settings->thumb_y;
safef(pixelStr, sizeof(pixelStr), "%d,%d", x1, y1);
if ((el = hashLookup(pixelHash, pixelStr)) == NULL)
{
hm = AllocA(struct hmPixel);
hm->x = x1;
hm->y = y1;
hm->w = ((x2 - x1) > 0) ? (x2 - x1) : 1;
hm->h = ((y2 - y1) > 0) ? (y2 - y1) : 1;
hm->val = 0.0;
hm->count = 0;
slAddHead(&hmList, hm);
hashAdd(pixelHash, pixelStr, hm);
}
else
hm = el->val;
hm->val += val;
hm->count += 1.0;
}
for (hm = hmList; hm ; hm = hm->next)
{
val = hm->val / (double) hm->count;
absVal = fabs(val) * gain;
int colorIndex = (int)(absVal * (EXPR_DATA_SHADES-1.0) * colorScale);
/* Clip color index to fit inside of array, since we may have brightened it. */
if (colorIndex < 0)
colorIndex = 0;
if (colorIndex >= EXPR_DATA_SHADES)
colorIndex = EXPR_DATA_SHADES-1;
if(val >= 0)
valCol = upShades[colorIndex];
else
valCol = downShades[colorIndex];
vgBox(vg, hm->x, hm->y, hm->w, hm->h, valCol);
}
vgUnclip(vg);
}
void drawFeatureLabels(struct vGfx *vg, struct mapSettings *settings)
{
if (!settings)
return;
if (settings->hm_y_scale < settings->fontHeight)
return;
vgSetClip(vg, 0, settings->hm_y, settings->hm_x, settings->hm_height);
struct hashEl *el;
struct hashCookie cookie = hashFirst(settings->featureHash);
while ((el = hashNext(&cookie)) != NULL)
{
char *name = el->val;
int i = hashIntValDefault(settings->y_index, el->name, -1);
if (i < 0)
continue;
int y = round((double) i * settings->hm_y_scale) + settings->hm_y;
int offset = (settings->hm_y_scale - (double) settings->fontHeight)/2.0;
if (offset > 0)
y += offset;
vgTextRight(vg, 0, y, settings->hm_x - TEXT_BUFFER, settings->fontHeight, MG_BLACK,
settings->font, name);
}
}
void drawSampleLabels(struct vGfx *vg, struct mapSettings *settings)
{
if (!settings)
return;
if (settings->hm_x_scale < settings->fontHeight)
return;
vgSetClip(vg, settings->hm_x, 0, settings->hm_width, settings->hm_y);
struct hashEl *el;
struct hashCookie cookie = hashFirst(settings->sampleHash);
while ((el = hashNext(&cookie)) != NULL)
{
char *name = el->val;
int i = hashIntValDefault(settings->x_index, el->name, -1);
if (i < 0)
continue;
int x = ceil(((double) i) * settings->hm_x_scale) + settings->hm_x;
verticalTextLeft(vg, x, 0, round(settings->hm_x_scale), settings->hm_y - TEXT_BUFFER,
MG_BLACK, settings->fontHeight, settings->font, name);
}
}
void drawLabels(struct vGfx *vg, struct mapSettings *settings)
{
drawFeatureLabels(vg, settings);
drawSampleLabels(vg, settings);
}
void mapSettingsDefaultColor(struct mapSettings *settings)
{
struct rgbColor *low = AllocA(struct rgbColor);
struct rgbColor *zero = AllocA(struct rgbColor);
struct rgbColor *high = AllocA(struct rgbColor);
struct rgbColor *missing = AllocA(struct rgbColor);
// Default "Patriotic" Red, White, Blue
zero->r = 255;
zero->g = 255;
zero->b = 255;
high->r = 255;
high->g = 0;
high->b = 0;
low->r = 0;
low->g = 0;
low->b = 255;
// Light Gray for missing vals
missing->r = 200;
missing->g = 200;
missing->b = 200;
// Set colors in structure
settings->low = low;
settings->zero = zero;
settings->high = high;
settings->missing = missing;
}
int prepareSampleLabels(struct mapSettings *settings,
struct samples *samples)
{
if (!samples)
return MIN_LABEL_SIZE;
settings->sampleHash = hashNew(0);
int maxStrWidth = 0;
char id[128];
struct samples *sa;
for (sa = samples; sa; sa = sa->next)
{
safef(id, sizeof(id), "%d", sa->id);
char *name = cloneString(sa->sample_name);
hashAdd(settings->sampleHash, id, name);
int strWidth = mgFontStringWidth(settings->font, name);
if (strWidth > maxStrWidth)
maxStrWidth = strWidth;
}
/* add space around label */
maxStrWidth += TEXT_BUFFER;
if (maxStrWidth < MIN_LABEL_SIZE)
return MIN_LABEL_SIZE;
return maxStrWidth;
}
int prepareFeatureLabels(struct mapSettings *settings,
struct analysisFeatures *features)
{
if (!features)
return MIN_LABEL_SIZE;
settings->featureHash = hashNew(0);
int maxStrWidth = 0;
char id[128];
struct analysisFeatures *fe;
for (fe = features; fe; fe = fe->next)
{
safef(id, sizeof(id), "%d", fe->id);
char *name = cloneString(fe->feature_name);
hashAdd(settings->featureHash, id, name);
int strWidth = mgFontStringWidth(settings->font, name);
if (strWidth > maxStrWidth)
maxStrWidth = strWidth;
}
/* add space around label */
maxStrWidth += TEXT_BUFFER;
if (maxStrWidth < MIN_LABEL_SIZE)
return MIN_LABEL_SIZE;
return maxStrWidth;
}
struct mapSettings *initMapSettings(struct slName *saList, struct slName *feList,
struct samples *samples, struct analysisFeatures *features,
int width, int height)
/* saList - list of sample ids
* feList - list of analysis feature ids
* samples - info on samples
* afList - info on features
*/
{
if (!saList || !feList || !samples || !features)
return NULL;
struct mapSettings *settings = AllocA(struct mapSettings);
trackLayoutInit(&tl, cart);
settings->fontHeight = tl.fontHeight;
settings->font = tl.font;
settings->width = width;
settings->height = height;
int xlabelsize = prepareSampleLabels(settings, samples);
int ylabelsize = prepareFeatureLabels(settings, features);
settings->hm_x = xlabelsize;
settings->hm_y = ylabelsize;
settings->hm_width = width - settings->hm_x;
settings->hm_height = height - settings->hm_y;
settings->thumb_x = 5;
settings->thumb_y = 5;
settings->thumb_width = settings->hm_x - 2 * settings->thumb_x;
settings->thumb_height = settings->hm_y - 2 * settings->thumb_y;
mapSettingsDefaultColor(settings);
settings->max_deviation = DEFAULT_MAX_DEVIATION;
settings->gain = 1.0;
settings->x_index = hashNew(0);
settings->y_index = hashNew(0);
struct hashEl *el;
struct slName *sl;
int numFeatures = 0;
for (sl = feList; sl; sl = sl->next)
{
if ((el = hashLookup(settings->y_index, sl->name)) != NULL)
continue;
hashAddInt(settings->y_index, sl->name, numFeatures);
numFeatures += 1;
}
int numSamples = 0;
for (sl = saList; sl; sl = sl->next)
{
if ((el = hashLookup(settings->x_index, sl->name)) != NULL)
continue;
hashAddInt(settings->x_index, sl->name, numSamples);
numSamples += 1;
}
settings->hm_x_scale = (double) settings->hm_width / (double) numSamples;
settings->hm_y_scale = (double) settings->hm_height / (double) numFeatures;
settings->thumb_x_scale = (double) settings->thumb_width / (double) numSamples;
settings->thumb_y_scale = (double) settings->thumb_height / (double) numFeatures;
return settings;
}
char *heatmapGif(struct sqlConnection *conn, struct rawData *rdList,
struct mapSettings *settings)
/* Create genome GIF file and HT that includes it. */
{
if (!rdList || !settings)
return NULL;
-if (settings->height * settings->width == 0)
+if (settings->height <= 0 || settings->width <= 0)
return NULL;
struct hvGfx *vg;
struct tempName md5Tn;
char *strToHash = cartSettingsString(hghPrefix, "heatmapGif");
trashDirMD5File(&md5Tn, "hgg", ".gif", strToHash);
off_t size = fileSize(md5Tn.forCgi);
if (!fileExists(md5Tn.forCgi) || (size == 0) || DEBUG)
{
vg = hvGfxOpenGif(settings->width, settings->height, md5Tn.forCgi, FALSE);
drawRawData(vg->vg, rdList, settings);
drawLabels(vg->vg, settings);
hvGfxClose(&vg);
}
char *filename = replaceChars(md5Tn.forHtml, "..", "");
return filename;
}