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;
 }