src/hg/instinct/hgGeneset/drawingCode.c 1.4

1.4 2010/01/29 22:27:35 jsanborn
added labels/thumbnail
Index: src/hg/instinct/hgGeneset/drawingCode.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/instinct/hgGeneset/drawingCode.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -b -B -U 4 -r1.3 -r1.4
--- src/hg/instinct/hgGeneset/drawingCode.c	28 Jan 2010 23:19:35 -0000	1.3
+++ src/hg/instinct/hgGeneset/drawingCode.c	29 Jan 2010 22:27:35 -0000	1.4
@@ -30,11 +30,51 @@
 
 #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
@@ -102,9 +142,13 @@
 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, missingColor);
+//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;
@@ -114,15 +158,8 @@
 struct hmPixel *hm, *hmList = NULL;
 struct hashEl *el;
 struct hash *pixelHash = hashNew(0);
 
-int w = round(settings->x_scale);
-if (w < 1)
-    w = 1;
-int h = round(settings->y_scale);
-if (h < 1)
-    h = 1;
-
 struct rawData *rd;
 for (rd = rdList; rd; rd = rd->next)
     {
     val = rd->val;    
@@ -144,12 +181,13 @@
 
     if (i < 0 || j < 0)
 	continue;
 
-    int x1 = round((double) i * settings->x_scale);
-    int y1 = round((double) j * settings->y_scale);
-    int x2 = round((double) (i + 1) * settings->x_scale);
-    int y2 = round((double) (j + 1) * settings->y_scale);
+    /** 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)
 	{
@@ -164,9 +202,32 @@
 	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;
     }
 
@@ -192,8 +253,71 @@
     
 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);
@@ -224,18 +348,107 @@
 settings->high    = high;
 settings->missing = missing;
 }
 
-struct mapSettings *initMapSettings(struct slName *samples, struct slName *features, 
+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 (!samples || !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;
@@ -245,27 +458,30 @@
 
 struct hashEl *el;
 struct slName *sl;
 int numFeatures = 0;
-for (sl = features; sl; sl = sl->next)
+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 = samples; sl; sl = sl->next)
+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->x_scale = (double) width / (double) numSamples;
-settings->y_scale = (double) height / (double) numFeatures;
+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;
 }
 
@@ -290,8 +506,10 @@
     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, "..", "");